Pamiętaj również, że metody rozszerzające zostały dodane, aby pomóc zapytaniom Linq być bardziej czytelnymi, gdy są używane w ich stylu C #.
Te dwie afekty są absolutnie równoważne, ale pierwsza jest znacznie bardziej czytelna (a luka w czytelności oczywiście wzrośnie wraz z większą liczbą metod połączonych łańcuchowo).
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));
Zauważ, że w pełni kwalifikowana składnia powinna nawet wyglądać następująco:
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));
Przypadkowo parametry typu Where
i Last
nie muszą być jawnie wymieniane, ponieważ można je wywnioskować dzięki obecności pierwszego parametru z tych dwóch metod (parametr, który jest wprowadzany przez słowo kluczowe this
i czyni je metodami rozszerzającymi).
Ten punkt jest oczywiście zaletą (między innymi) metod rozszerzających i można z tego skorzystać w każdym podobnym scenariuszu, w którym występuje łączenie metod.
W szczególności jest to bardziej elegancki i przekonujący sposób, w jaki znalazłem metodę klasy bazowej wywoływaną przez dowolną podklasę i zwracającą silnie wpisane odniesienie do tej podklasy (z typem podklasy).
Przykład (ok, ten scenariusz jest totalnie kiepski): po dobrej nocy zwierzę otwiera oczy, po czym płacze; każde zwierzę otwiera oczy w ten sam sposób, podczas gdy pies szczeka i kwaks kaczka.
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();
}
}
Koncepcyjnie OpenTheEyes
powinna być Animal
metoda, ale to potem powrócić instancję klasy abstrakcyjnej Animal
, która nie zna konkretnych metod podklasy jak Bark
lub Duck
czy cokolwiek innego. Dwie linie skomentowane jako * 1 i * 2 spowodowałyby wtedy błąd kompilacji.
Ale dzięki metodom rozszerzającym możemy mieć coś w rodzaju „metody bazowej, która zna typ podklasy, w której jest wywoływana”.
Zauważ, że prosta metoda ogólna mogłaby wykonać zadanie, ale w znacznie bardziej niezręczny sposób:
public abstract class Animal
{
public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
{
return (TAnimal)this;
}
}
Tym razem nie ma parametru, a zatem nie ma możliwości wnioskowania o typie zwracanym. Wezwanie może być niczym innym jak:
Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();
... co może znacznie ważyć kod, jeśli w grę wchodzi więcej łańcuchów (zwłaszcza wiedząc, że parametr typu zawsze będzie <Dog>
w wierszu Goofy'ego i <Duck>
Donalda ...)