Rappelez-vous également que les méthodes d'extension ont été ajoutées pour aider les requêtes Linq à être plus lisibles lorsqu'elles sont utilisées dans leur style C #.
Ces 2 affectations sont absolument équivalentes, mais la première est beaucoup plus lisible (et l'écart de lisibilité augmenterait bien sûr avec plus de méthodes enchaînées).
int n1 = new List<int> {1,2,3}.Where(i => i % 2 != 0).Last();
int n2 = Enumerable.Last(Enumerable.Where(new List<int> {1,2,3}, i => i % 2 != 0));
Notez que la syntaxe complète devrait même être:
int n1 = new List<int> {1,2,3}.Where<int>(i => i % 2 != 0).Last<int>();
int n2 = Enumerable.Last<int>(Enumerable.Where<int>(new List<int> {1,2,3}, i => i % 2 != 0));
Par hasard, les paramètres de type de Where
et Last
n'ont pas besoin d'être explicitement mentionnés car ils peuvent être déduits grâce à la présence du premier paramètre de ces deux méthodes (le paramètre qui est introduit par le mot-cléthis
et en fait des méthodes d'extension).
Ce point est évidemment un avantage (entre autres) des méthodes d'extension, et vous pouvez en profiter dans tous les scénarios similaires où le chaînage de méthodes est impliqué.
Surtout, c'est la façon la plus élégante et convaincante que j'ai trouvée d'avoir une méthode de classe de base invocable par n'importe quelle sous-classe et de renvoyer une référence fortement typée à cette sous-classe (avec le type de sous-classe).
Exemple (ok, ce scénario est totalement ringard): après une bonne nuit, un animal ouvre les yeux puis pousse un cri; chaque animal ouvre les yeux de la même manière, tandis qu'un chien aboie et un canard kwaks.
public abstract class Animal
{
}
public static class AnimalExtension
{
public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
{
return animal;
}
}
public class Dog : Animal
{
public void Bark() { }
}
public class Duck : Animal
{
public void Kwak() { }
}
class Program
{
static void Main(string[] args)
{
Dog Goofy = new Dog();
Duck Donald = new Duck();
Goofy.OpenTheEyes().Bark();
Donald.OpenTheEyes().Kwak();
}
}
Conceptuellement, OpenTheEyes
devrait être une Animal
méthode, mais elle retournerait alors une instance de la classe abstraite Animal
, qui ne connaît pas les méthodes de sous-classe spécifiques comme Bark
ou Duck
ou autre. Les 2 lignes commentées * 1 et * 2 soulèveraient alors une erreur de compilation.
Mais grâce aux méthodes d'extension, on peut avoir une sorte de "méthode de base qui connaît le type de sous-classe sur lequel elle est appelée".
Notez qu'une méthode générique simple aurait pu faire le travail, mais d'une manière beaucoup plus délicate:
public abstract class Animal
{
public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
{
return (TAnimal)this;
}
}
Cette fois, pas de paramètre et donc pas d'inférence de type de retour possible. L'appel ne peut être rien d'autre que:
Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();
... qui peut peser beaucoup sur le code si plus de chaînage est impliqué (surtout en sachant que le paramètre type sera toujours <Dog>
sur la ligne de Goofy et <Duck>
sur celle de Donald ...)