También recuerde que los métodos de extensión se agregaron como una forma de ayudar a que las consultas de Linq sean más legibles cuando se usan en su estilo C #.
Estas 2 afectaciones son absolutamente equivalentes, pero la primera es mucho más legible (y la brecha en la legibilidad, por supuesto, aumentaría con más métodos encadenados).
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));
Tenga en cuenta que la sintaxis completa debería ser incluso:
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));
Por casualidad, los parámetros de tipo de Where
y Last
no necesitan ser mencionados explícitamente ya que se pueden inferir gracias a la presencia del primer parámetro de estos dos métodos (el parámetro que es introducido por la palabra clavethis
y los convierte en métodos de extensión).
Este punto es obviamente una ventaja (entre otras) de los métodos de extensión, y puede beneficiarse de él en todos los escenarios similares en los que esté involucrado el encadenamiento de métodos.
Especialmente, es la forma más elegante y convincente que encontré de tener un método de clase base invocable por cualquier subclase y devolver una referencia fuertemente tipada a esta subclase (con el tipo de subclase).
Ejemplo (ok, este escenario es totalmente cursi): después de una buena noche, un animal abre los ojos y luego da un grito; todos los animales abren los ojos de la misma manera, mientras que un perro ladra y un pato kwak.
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();
}
}
Conceptualmente OpenTheEyes
debería ser un Animal
método, pero luego devolvería una instancia de la clase abstracta Animal
, que no conoce métodos de subclase específicos comoBark
o Duck
lo que sea. Las 2 líneas comentadas como * 1 y * 2 generarían un error de compilación.
Pero gracias a los métodos de extensión, podemos tener una especie de "método base que conoce el tipo de subclase en el que se llama".
Tenga en cuenta que un método genérico simple podría haber hecho el trabajo, pero de una manera mucho más incómoda:
public abstract class Animal
{
public TAnimal OpenTheEyes<TAnimal>() where TAnimal : Animal
{
return (TAnimal)this;
}
}
Esta vez, no hay parámetro y, por lo tanto, no es posible inferencia del tipo de retorno. La llamada no puede ser otra que:
Goofy.OpenTheEyes<Dog>().Bark();
Donald.OpenTheEyes<Duck>().Kwak();
... lo que puede pesar mucho el código si se involucra más encadenamiento (especialmente sabiendo que el parámetro de tipo siempre estará <Dog>
en la línea de Goofy y <Duck>
en la de Donald ...)