posté @ Friday, October 09, 2009 1:03 PM

C# 4.0 introduit une nouveauté très intéressante au niveau des déclarations d’interfaces / délégués génériques : la notion de Co-variance et Contra-variance.

La co-variance permet de dire que si un type implémente IMonInterface<TypeDérivé>, il implémente aussi IMonInterface<TypeParent>.

Pour la contra-variance, c’est exactement le contraire.

Du coup, les développeurs de la BCL ont modifié la déclaration de pas mal d’interfaces et délégués génériques pour profiter de ces règles : ainsi tout IEnumerable<string> implémente automatiquement IEnumerable<object> et tout délégué de type Action<object> peut être utilisé à la place d’un Action<string>.

 

En fait cette notion de co-variance / contra-variance, bien que non déclarable en C# et VB dans leur versions actuelle est présente dans les spécifications du langage MSIL depuis .Net 2.0, et si l’on en a le courage, on peut tout a fait créer des interfaces / délégués co/contra-variants. Pour ceci, il faut simplement indiquer un “+” (co-variant) ou un “-“ (contra-variant) devant le paramètre du type. Voici un exemple avec 2 interfaces définie en MSIL :

.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) 
  .ver 2:0:0:0
}
.assembly Cotypes
{
  .ver 0:0:0:0
}
.module Cotypes.dll


// +T indique que l'interface est CoVariante (toute implementation de IValueProvider<ChildClass>
// implemente IValueProvider<ParentClass>) .class interface public abstract auto ansi Cotypes.IValueProvider`1<+T> { .method public hidebysig newslot abstract virtual instance !T GetValue() cil managed { } } // -T indique que l'interface est ContraVariante (toute implementation de IValueConsumer<ParentClass>
// implemente IValueConsumer<ChildClass>) .class interface public abstract auto ansi Cotypes.IValueConsumer`1<-T> { .method public hidebysig newslot abstract virtual instance void SetValue(!T 'value') cil managed { } }

Une fois compilée sous forme de DLL, on peut alors implémenter ces interfaces en C# et profiter la co/contra-variance :

static void Main(string[] args)
{
    IValueProvider<string> stringProvider = new StringValueProvider("HelloWorld");
    IValueProvider<object> objProvider = stringProvider as IValueProvider<object>;

    IValueConsumer<object> objConsumer = new ConsoleDisplayer();
    IValueConsumer<string> stringConsumer = objConsumer as IValueConsumer<string>;

    objConsumer.SetValue(objProvider.GetValue());
    stringConsumer.SetValue(stringProvider.GetValue());
}

En C# 4.0, la seule différence (en dehors du fait que l’écriture de telles interfaces peut se faire directement en C#) est qu’il n’y a pas besoin du mot-clef as, l’opération de cast se fait implicitement.

Vous pouvez télécharger les sources de cet exemple ici.


Commentaires :

No comments posted yet.

Ecrire un commentaire :

Titre :*
Nom *
Email
Url
Commentaire : *  


Please add 4 and 2 and type the answer here: