Denken Sie auch daran, dass Erweiterungsmethoden hinzugefügt wurden, um die Lesbarkeit der Linq-Abfrage zu verbessern, wenn sie in ihrem C # -Stil verwendet wird.
Diese beiden Affekte sind absolut gleichwertig, aber der erste ist weitaus besser lesbar (und die Lücke in der Lesbarkeit würde sich natürlich mit mehr verketteten Methoden vergrößern).
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));
Beachten Sie, dass die vollständig qualifizierte Syntax sogar wie folgt lauten sollte:
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));
Zufällig müssen die Typparameter von Where
und Last
müssen nicht explizit erwähnt werden, da sie dank des Vorhandenseins des ersten Parameters dieser beiden Methoden abgeleitet werden können (der Parameter, der durch das Schlüsselwort eingeführt wird this
und sie zu Erweiterungsmethoden macht).
Dieser Punkt ist offensichtlich (unter anderem) ein Vorteil der Erweiterungsmethoden, und Sie können ihn in jedem ähnlichen Szenario nutzen, in dem es um die Verkettung von Methoden geht.
Insbesondere ist es die elegantere und überzeugendere Art, eine Basisklassenmethode zu finden, die von jeder Unterklasse aufgerufen werden kann und einen stark typisierten Verweis auf diese Unterklasse (mit dem Unterklassentyp) zurückgibt.
Beispiel (ok, dieses Szenario ist total kitschig): Nach einer guten Nacht öffnet ein Tier die Augen und schreit dann; Jedes Tier öffnet die Augen auf die gleiche Weise, während ein Hund bellt und eine Ente kwaks.
public abstract class Animal
{
//some code common to all animals
}
public static class AnimalExtension
{
public static TAnimal OpenTheEyes<TAnimal>(this TAnimal animal) where TAnimal : Animal
{
//Some code to flutter one's eyelashes and then open wide
return animal; //returning a self reference to allow method chaining
}
}
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(); //*1
Donald.OpenTheEyes().Kwak(); //*2
}
}
Konzeptionell OpenTheEyes
sollte es sich um eine Animal
Methode handeln, die jedoch eine Instanz der abstrakten Klasse zurückgibt Animal
, die bestimmte Unterklassenmethoden wie Bark
oder Duck
oder was auch immer nicht kennt . Die 2 als * 1 und * 2 kommentierten Zeilen würden dann einen Kompilierungsfehler auslösen.
Dank der Erweiterungsmethoden können wir jedoch eine Art "Basismethode haben, die den Unterklassentyp kennt, auf dem sie aufgerufen wird".
Beachten Sie, dass eine einfache generische Methode die Arbeit hätte erledigen können, aber auf eine viel umständlichere Weise:
public abstract class Animal
{
//some code common to all animals
public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
{
//Some code to flutter one's eyelashes and then open wide
return (TAnimal)this; //returning a self reference to allow method chaining
}
}
Diesmal kein Parameter und damit keine mögliche Rückgabe des Rückgabetyps. Der Anruf kann nichts anderes sein als:
Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();
... was den Code sehr wiegen kann, wenn mehr Verkettung erforderlich ist (insbesondere in dem Wissen, dass der Typparameter immer in <Dog>
Goofys Zeile und <Duck>
in Donalds Zeile steht ...)