Также помните, что методы расширения были добавлены как способ сделать запрос Linq более читабельным при использовании в их стиле C #.
Эти две модификации абсолютно эквивалентны, но первая гораздо более читабельна (и разрыв в читаемости, конечно, увеличится с увеличением количества связанных методов).
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));
Обратите внимание, что полный синтаксис должен быть даже таким:
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));
Случайно, параметры типа Where
и Last
не нуждаются в явном упоминании, поскольку они могут быть переданы благодаря наличию первого параметра этих двух методов (параметр, который вводится ключевым словомthis
и делает их методами расширения).
Этот момент, очевидно, является преимуществом (среди прочего) методов расширения, и вы можете воспользоваться им в любом подобном сценарии, где задействовано объединение методов.
В частности, это более элегантный и убедительный способ, которым я нашел метод базового класса, вызываемый любым подклассом и возвращающий строго типизированную ссылку на этот подкласс (с типом подкласса).
Пример (хорошо, этот сценарий совершенно дрянной): после спокойной ночи животное открывает глаза, а затем кричит; каждое животное открывает глаза одинаково, тогда как собака лает, а утка квакает.
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();
}
}
Концептуально OpenTheEyes
должен быть Animal
метод, но он затем возвращает экземпляр абстрактного класса Animal
, который не знает конкретных методов подкласса , как Bark
или Duck
или любой другой . Две строки, отмеченные как * 1 и * 2, вызовут ошибку компиляции.
Но благодаря методам расширения у нас может быть своего рода «базовый метод, который знает тип подкласса, для которого он вызывается».
Обратите внимание, что простой универсальный метод мог бы выполнить эту работу, но гораздо более неудобным способом:
public abstract class Animal
{
public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
{
return (TAnimal)this;
}
}
На этот раз без параметра и, следовательно, без возможного вывода типа возвращаемого значения. Вызов не может быть ничем иным, как:
Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();
... который может сильно повесить код, если задействовано больше цепочек (особенно зная, что параметр типа всегда будет <Dog>
в строке Гуфи и в строке <Duck>
Дональда ...)