Jiri's Shared IT knowledge

jeudi, juillet 14, 2005

Innovations du langage C# 2.0

Attaquons les innovations d’un premier langage : « C# », entendez C Sharp. Nous aborderons les Générics, les classes partielles, les méthodes anonymes, les itérateurs, les qualifications d'alias et des classes statiques, ... Les explications se voudront relativement simples en vue de permettre une compréhension rapide de ces avancées.

1. Générics
Présenté comme étant une des caractéristiques prépondérantes, soutenue par Bill Gates, du C# 2.0, les Générics apportent la capacité de réutilisation générique des algorithmes. Couramment utilisé en C++ comme Template, l’absence des Générics sous C# fut un reproche des utilisateurs auprès de Microsoft. Avant l’apparition des génériques, nous aurions écrit le code suivant :

public class List
{
private object[] elements;
private int count;
public void Add(object element) {
if (count == elements.Length) Resize(count * 2);
elements[count++] = element;
}
public object this[int index] {
get { return elements[index]; }
set { elements[index] = value; }
}
public int Count {
get { return count; }
}
}
En l’exploitant de la manière suivante : List intList = new List();
intList.Add(1); // Boxing
intList.Add(2); // Boxing
intList.Add("Three"); // Génère une erreur à l’exploitation du code
int i = (int)intList[0]; // Le Cast est nécessaire
Cette méthodologie, bien que fonctionnelle, soulève quelques problématiques : le transtypage explicite nécessaire, la diminution des performances de ce code lors de l’étape implicite de boxing et unboxing des éléments, la possibilité d’empiler des éléments de types différents pouvant engendrer des problématiques de transtypages uniquement décelables lors de l'exécution du code, nous parlerons dans ce cas de code non Type-Safe. L’entrée des Générics permet d’écrire le code suivant : public class List
{
private T[] elements;
private int count;
public void Add(T element) {
if (count == elements.Length) Resize(count * 2);
elements[count++] = element;
}
public T this[int index] {
get { return elements[index]; }
set { elements[index] = value; }
}
public int Count {
get { return count; }
}
}
Et une exploitation : List intList = new List();
intList.Add(1); // Pas de boxing
intList.Add(2); // Pas de boxing
intList.Add("Three"); // Erreur levee lors de la compilation
int i = intList[0]; // Aucun Cast nécessaire

Cette nouveauté permet ainsi d’éviter les problématiques mentionnées sans utilisation des Générics. Nous avons ainsi un code performant, Type-Safe n'engendrant aucune opération de boxing/unboxing sans transtypage des éléments.

2. Classes partielles

Les classes partielles représentent une nouveauté intéressante et plus particulièrement dans le cadre du travail collaboratif. L’approche est simple, à l’avenir les classes pourront être écrites sur plusieurs fichiers grâce à la syntaxe " public partial class …"

Exemple : // ClassPart1.cs
using System;
public partial class ClassPart
{
public void Methode()
{
}
}


// ClassPart2.cs
using System;
public partial class ClassPart
{
public void AutreMethode()
{
}
}

Une nuance à ce titre entre VB.NET et C#, VB.NET tolère une classe déclarée en «Public Class».

3. Méthodes anonymes
Les méthodes anonymes permettent d'instancier une méthode sans la nommer. Contrairement aux Générics, cette nouveauté n'engendre aucune nouvelle implémentation au sein de l’IL (Intermediate Language) mais se situe au niveau du compilateur.

button.Click += delegate { MessageBox.Show("HelloWorld"); };

L’exemple suivant montre une méthode anonyme avec des arguments.

button.Click += delegate(object sender, EventArgs e) {
MessageBox.Show(((Button)sender).Text);
};

4. Itérateurs
Les itérateurs peuvent être considérés comme étant la contre partie logique C# 2.0 du foreach sous C#. Les itérateurs simplifient le processus d'itération de collection.

En C#, il est relativement simple de itérer des collections finies d’éléments en utilisant le mot clé « foreach ». Ceci nécessite l’implémentation des interfaces IEnumerable et IEnumerator.

L’objectif premier des itérateurs est d'alléger le travail d'écriture des zones fixes de code et de rendre plus simple l'exposition de collection énumérables.

class List: IEnumerable
{
private T[] elements;
public IEnumerator GetEnumerator()
{
foreach (T element in elements)
{
yield element;
}
}
}


Nous n’oublierons pas d’implémenter « System.Collections.Generic » afin d’exploiter la version Generic de IEnumerator : « using System.Collections.Generic; »

Vous aurez aussi remarqué l’utilisation d’un nouveau mot clé « yield » qui est utilisé dans notre méthode comme retour de IEnumerator, IEnumerable ou tout autre Generic équivalent. Tout comme les méthodes anonymes, les itérateurs gèrent des arguments.

5. Qualificateur global d’espace de nom
L’accès à un membre dans l’espace de nom global s’avère être utile quand le membre pourrait être caché par une autre entité du même nom.

Le qualificateur global d’espace de nom résout ce problème par l’introduction de l'opérateur ": :" qui peut être employé comme espace de nom ou type préfixe nommé.

namespace Acme
{
namespace System
{
class Example
{
static void Main()
{
::System.Console.WriteLine("Hello");
}
}
}
}


6. Classes Statiques
Les classes statiques prévoient de remplacer les design patterns de création de classes de type « sealed » avec un constructeur "private" contenant uniquement des méthodes statiques.
Le code suivant :

public sealed class Environment
{
// Keep class from being created
private Environment() { }
}

Devient :
public static sealed class Environment
{
}

L'avantage d'employer une classe statique au lieu du modèle de conception ci-dessus est que le compilateur peut maintenant rapporter une erreur si des instances de méthodes sont accidentellement déclarées

7. Types Nullables
Si vous n’aviez jamais imaginé assigner des valeurs nulles à vos types « valeur », détrompez-vous ! Vous pouvez désormais assigner des valeurs nulles à des types valeur en définissant un type nullable. Ceci en ajoutant un point d'interrogation "?" immédiatement à droite du type lors de la définition de votre variable. Les types nullables dérivent du type générique System.Nullable. T étant votre type. Deux propriétés étendent l'utilité des types nullables : "HasValue" et "Value". HasValue évalue le caractère « nulle » de votre type tandis que Value retournera la valeur fondamentale que celle-ci soit nulle ou non.

//les deux déclarations suivantes se réfère au même type nullable d'interger
int? myInt1 = null; Nullable myInt2 = null;
if (myInt1 == null) Console.WriteLine("myInt1 is null."); if (myInt2 == null) Console.WriteLine("myInt2 is null.");
// Nous pouvons examiner le type valeur pour nous assurer d’obtenir une valeur de cette manière
myInt1 = 1; if (myInt1.HasValue) Console.WriteLine("myInt1 has a value = {0}", myInt1.Value); else Console.WriteLine("myInt1 is null.");