Les méthodes d'extension de C# 3: réponse au quizz

Réponse au post: https://blogs.msdn.com/mitsufu/archive/2007/07/23/geek-quizz.aspx

Alors, alors..., le code étrange de la méthode d'extension .AsEnumerable(), bien que paraissant inutile à la première lecture sert bien à quelque chose.

Le point technique très propre aux méthodes d'extension est le suivant:

Une méthode d'extension étend les fonctionnalités d'une classe depuis "l'extérieur". Etant juste une simplification d'écriture, cette technique ne casse pas les règles objets.

Ainsi, ces méthodes sont forcément publiques et n'accèdent qu'à la visibilité publique de l'objet qu'elles étendent.

La syntaxe de la méthode statique:

MyExtensions.AsEnumerable(obj);

se simplifie tout simplement en:

obj.AsEnumerable();

On remarque cependant que MyExtensions n'apparait pas dans la seconde syntaxe. AsEnumerable doit donc être déterminé par le compilateur. Comment s'y prendre ?

Plusieurs questions en découlent:

  • plusieurs méthodes d'extensions peuvent étendre un même type.
  • plusieurs méthodes d'extensions peuvent étendre des types appartenant à la même hiérarchie de classes

Première conclusion, il peut y avoir des conflits !

En effet, les méthodes d'extensions sont recherchées par le compilateur dans la portée courante.

  • Si plusieurs solutions sont trouvées pour un même type alors une erreur de compilation est soulevée.
  • Si plusieurs solutions sont trouvées pour différents types dans la même hiérarchie (classes ou interfaces) alors c'est le type de la référence qui compte et non celui de l'instance !!!

Ainsi, si l'on implémente deux méthodes d'extensions identiques sur deux types héritant l'un de l'autre:

 public class A { }

public class B : A { }

public static class MyExtensions 
{
    //Etend la classe A
    public static void Test(this A a) {   ...   }
    public static void Test(this B b) {   ...   }
}

B b = new B();
b.Test();

appelle la méthode qui étend B

A a = b;
a.Test();

appelle la méthode qui étend A, alors que l'instance est bien de type B.

Pour conclure, nous avons le même pattern avec les interfaces IEnumerable<T> et IQueryable<T>. Quasiment toutes les fonctionnalités de Linq (where, orderby, select, count, etc) étendent ces deux interfaces.

La méthode .AsEnumerable(), en ne faisant que renvoyer une référence typée en IEnumerable<T> depuis n'importe quelle object implémentant cette interface permet de forcer l'appelle des méthodes d'extension de ce type.

Mitsu