¿Qué ventajas de los métodos de extensión ha encontrado?

Un “no creyente” de C # me preguntaba cuál era el propósito de los métodos de extensión. Le expliqué que podría agregar nuevos métodos a objetos que ya estaban definidos, especialmente cuando no posee / controla la fuente del objeto original.

Él mencionó “¿Por qué no simplemente agregas un método a tu propia clase?” Hemos estado dando vueltas y vueltas (en el buen sentido). Mi respuesta general es que es otra herramienta en el toolbelt, y su respuesta es que es una pérdida inútil de una herramienta … pero pensé que obtendría una respuesta más “ilustrada”.

¿Cuáles son algunos escenarios en los que ha utilizado métodos de extensión que no podría (o no debería haber usado) utilizar un método agregado a su propia clase?

Creo que los métodos de extensión ayudan mucho al escribir código, si agrega métodos de extensión a tipos básicos, los obtendrá rápidamente en el intellisense.

Tengo un proveedor de formatos para formatear un tamaño de archivo . Para usarlo necesito escribir:

 Console.WriteLine(String.Format(new FileSizeFormatProvider(), "{0:fs}", fileSize)); 

Creando un método de extensión que puedo escribir:

 Console.WriteLine(fileSize.ToFileSize()); 

Más limpio y simple.

La única ventaja de los métodos de extensión es la legibilidad del código. Eso es.

Los métodos de extensión le permiten hacer esto:

 foo.bar(); 

en lugar de esto:

 Util.bar(foo); 

Ahora hay muchas cosas en C # que son así. En otras palabras, hay muchas características en C # que parecen triviales y que no tienen un gran beneficio en sí mismas. Sin embargo, una vez que comienzas a combinar estas características, comienzas a ver algo un poco más grande que la sum de sus partes. LINQ se beneficia enormemente de los métodos de extensión, ya que las consultas LINQ serían casi ilegibles sin ellos. LINQ sería posible sin métodos de extensión, pero no es práctico.

Los métodos de extensión se parecen mucho a las clases parciales de C #. Por sí mismos no son muy útiles y parecen triviales. Pero cuando comienzas a trabajar con una clase que necesita código generado, las clases parciales comienzan a tener mucho más sentido.

¡No te olvides de las herramientas! Cuando agrega un método de extensión M en el tipo Foo, obtiene ‘M’ en la lista intellisense de Foo (suponiendo que la clase de extensión está dentro del scope). Esto hace que ‘M’ sea mucho más fácil de encontrar que MyClass.M (Foo, …).

Al final del día, es simplemente azúcar sintáctica para otros métodos estáticos, pero como comprar una casa: “ubicación, ubicación, ubicación”. Si se cuelga en el tipo, la gente lo encontrará!

Dos beneficios más de los métodos de extensión que he encontrado:

  • una interfaz fluida se puede encapsular en una clase estática de métodos de extensión, logrando así una separación de preocupaciones entre la clase principal y sus extensiones fluidas; He visto que lograr una mayor facilidad de mantenimiento
  • los métodos de extensión pueden colgarse de las interfaces, lo que le permite especificar un contrato (a través de una interfaz) y una serie asociada de comportamientos basados ​​en la interfaz (a través de métodos de extensión), nuevamente ofreciendo una separación de preocupaciones.

Algunos de los mejores usos que tuve para los métodos de extensión es la capacidad de:

  1. Ampliar la funcionalidad en objetos de terceros (ya sean comerciales o internos para mi empresa, pero gestionados por un grupo separado), que en muchos casos se marcarán como sealed .
  2. Crear funcionalidad predeterminada para las interfaces sin tener que implementar una clase abstracta

Tomemos como ejemplo, IEnumerable . Si bien es rico en métodos de extensión, me pareció molesto no implementar un método ForEach genérico. Entonces, hice mi propia cuenta:

 public void ForEach(this IEnumerable enumerable, Action action) { foreach ( var o in enumerable ) { action(o); } } 

Voila, todos mis IEnumerable independientemente del tipo de implementación, y si lo escribí o alguien más lo hice ahora tengo un método ForEach agregando una statement “using” apropiada en mi código.

Una de las grandes razones para usar métodos de extensión es LINQ. Sin métodos de extensión, mucho de lo que puede hacer en LINQ sería muy difícil. Los métodos de extensión Where (), Contains (), Select significan que se agrega mucha más funcionalidad a los tipos existentes sin cambiar su estructura.

Hay muchas respuestas sobre las ventajas de los métodos de extensión; ¿Qué tal si abordamos las desventajas ?

La mayor desventaja es que no hay error ni advertencia del comstackdor si tiene un método regular y un método de extensión con la misma firma en el mismo contexto.

Supongamos que crea un método de extensión que se aplica a una clase en particular. Luego, más tarde, alguien crea un método con una firma idéntica en esa clase.

Su código se comstackrá, y es posible que ni siquiera obtenga un error de tiempo de ejecución. Pero ya no estás ejecutando el mismo código que antes.

Interfaces fluidas y sensibilidad de contexto como lo demostró Greg Young en CodeBetter

Mi argumento personal para los métodos de extensión es que encajan muy bien en un diseño de OOP: considere el método simple

 bool empty = String.IsNullOrEmpty (myString) 

en comparación con

 bool empty = myString.IsNullOrEmpty (); 

Hay montones de excelentes respuestas sobre lo que los métodos de extensión le permiten hacer.

Mi respuesta corta es que casi eliminan la necesidad de fábricas.

Simplemente señalaré que no son un concepto nuevo y una de las mayores validaciones de ellos es que son una característica clave en Objective-C ( categorías ). Agregan tanta flexibilidad al desarrollo basado en el marco que NeXT tenía a los modeladores financieros de NSA y Wall Street como usuarios principales.

REALbasic también los implementa como métodos extendidos y han tenido un uso similar allí simplificando el desarrollo.

Me gustaría apoyar las otras respuestas aquí que mencionan la legibilidad mejorada del código como una razón importante detrás de los métodos de extensión. Lo demostraré con dos aspectos de esto: encadenamiento de métodos frente a llamadas de métodos nesteds y desorden de una consulta LINQ con nombres de clases estáticos sin sentido.


Tomemos esta consulta LINQ como un ejemplo:

 numbers.Where(x => x > 0).Select(x => -x) 

Tanto Where como Select son métodos de extensión, definidos en la clase estática Enumerable . Por lo tanto, si los métodos de extensión no existieran, y estos fueran métodos estáticos normales, la última línea de código tendría que verse esencialmente así:

 Enumerable.Select(Enumerable.Where(numbers, x => x > 0), x => -x) 

Vea cuánto más desagradable que acaba de obtener esa consulta.


En segundo lugar, si ahora quisiera introducir su propio operador de consulta, naturalmente no tendría forma de definirlo dentro de la clase estática de Enumerable , como todos los otros operadores de consulta estándar, porque Enumerable está en el marco y no tiene control sobre esa clase . Por lo tanto, tendría que definir su propia clase estática que contenga métodos de extensión. A continuación, puede obtener consultas como esta:

 Enumerable.Select(MyEnumerableExtensions.RemoveNegativeNumbers(numbers), x => -x) // ^^^^^^^^^^^^^^^^^^^^^^ // different class name that has zero informational value // and, as with 'Enumerable.xxxxxx', only obstructs the // query's actual meaning. 

Es cierto que puede agregar su método (extensión) directamente en su clase. Pero no todas las clases están escritas por ti. Las clases de la biblioteca central o de las bibliotecas de terceros a menudo se cierran y sería imposible obtener el azúcar sintáctico sin métodos de extensión. Pero recuerde, los métodos de extensión son como métodos independientes (estáticos) en, por ejemplo. c ++

Los métodos de extensión también pueden ayudar a mantener limpias sus clases y dependencias de clase. Por ejemplo, es posible que necesite un método Bar () para la clase Foo en todos los lugares donde se usa Foo. Sin embargo, es posible que desee un método .ToXml () en otro ensamblaje y solo para ese ensamblaje. En ese caso, puede agregar las dependencias System.Xml y / System.Xml.Linq necesarias en ese ensamblaje y no en el ensamblaje original.

Beneficios: las dependencias en su conjunto de clase de definición se reducen a las necesidades básicas y se evitará que otros ensamblados consumidores utilicen el método ToXml (). Vea esta presentación de PDC para mayor referencia.

Los métodos de extensión son realmente la incorporación .NET del refactor “Introducir método extranjero” del Libro de Martin Fowler (hasta la firma del método). Vienen básicamente con los mismos beneficios y trampas. En la sección de este refactor, dice que son una alternativa para cuando no se puede modificar la clase que realmente debería ser propietaria del método.

Veo principalmente los métodos de extensión como una admisión de que tal vez no deberían haber desautorizado las funciones gratuitas.

En la comunidad de C ++, a menudo se considera una buena práctica de POO preferir las funciones gratuitas de no miembros sobre los miembros, porque estas funciones no rompen la encapsulación al obtener acceso a miembros privados que no necesitan. Los métodos de extensión parecen ser una forma indirecta de lograr lo mismo. Es decir, una syntax más limpia para funciones estáticas que no tienen acceso a miembros privados.

Los métodos de extensión no son más que azúcar sintáctico, pero no veo ningún daño al usarlos.

  • Intellisense en el objeto en sí mismo en lugar de tener que llamar a alguna función de utilidad fea
  • Para las funciones de conversión, puede cambiar “XToY (X x)” a “ToY (esta X x)”, lo que resulta en bastante x.ToY () en lugar de feo XToY (x).
  • Extiende las clases sobre las que no tienes control
  • Extender la funcionalidad de las clases cuando no es deseable agregar métodos a las clases mismas. Por ejemplo, puede mantener los objetos de negocio simples y sin lógica, y agregar lógica empresarial específica con dependencias desagradables en los métodos de extensión

Los uso para reutilizar mis clases de modelo de objetos. Tengo un montón de clases que representan objetos que tengo en una base de datos. Estas clases se usan solo en el lado del cliente para mostrar los objetos, por lo que el uso básico es acceder a las propiedades.

 public class Stock { public Code { get; private set; } public Name { get; private set; } } 

Debido a ese patrón de uso, no quiero tener métodos de lógica de negocios en estas clases, por lo que hago que cada lógica comercial sea un método de extensión.

 public static class StockExtender { public static List  GetQuotesByDate(this Stock s, DateTime date) {...} } 

De esta forma puedo usar las mismas clases para el procesamiento de lógica de negocios y para la visualización de la interfaz de usuario sin sobrecargar el lado del cliente con código innecesario.

Una cosa interesante acerca de esta solución es que mis clases de modelo de objetos se generan dinámicamente usando Mono.Cecil , por lo que sería muy difícil agregar métodos de lógica de negocios, incluso si quisiera. Tengo un comstackdor que lee archivos de definición XML y genera estas clases de stubs que representan algún objeto que tengo en la base de datos. El único enfoque en este caso es extenderlos.

Acepto que los métodos de extensión aumentan la legibilidad del código, pero en realidad no es más que métodos estáticos de ayuda.

IMO utilizando métodos de extensión para agregar comportamiento a sus clases puede ser:

Confuso: los progtwigdores pueden creer que los métodos son parte del tipo extendido, por lo tanto, no comprenden por qué los métodos se han ido cuando el espacio de nombres de extensión no se importa.

Un antipatín: decide agregar el comportamiento a los tipos en su marco utilizando métodos de extensión, y luego enviárselos a una persona para que los pruebe en unidades. Ahora está atrapado en un marco que contiene un montón de métodos que no puede fingir.

Permite a C # soportar mejor los lenguajes dynamics, LINQ y una docena de otras cosas. Mira el artículo de Scott Guthrie .

En mi último proyecto, utilicé el método de extensión para adjuntar los métodos Validate () a los objetos comerciales. Lo justifiqué porque los objetos comerciales donde los objetos de transferencia serializables se usarán en diferentes dominios, ya que son entidades generales de comercio electrónico como producto, cliente, comerciante, etc. Bueno, en dominios diferentes, las reglas comerciales también pueden ser diferentes, así que encapsulé mi Lógica de validación atrasada en un método Validate atacado a la clase base de mis objetos de transferencia de datos. Espero que esto tenga sentido 🙂

Un caso en el que los métodos de extensión fueron bastante útiles fue en una aplicación cliente que usa servicios web ASMX. Debido a la serialización, los tipos de devolución de los métodos web no contienen ningún método (solo las propiedades públicas de estos tipos están disponibles en el cliente).

Los métodos de extensión permiten el uso para agregar funcionalidad (en el lado del cliente) a los tipos devueltos por los métodos web sin tener que crear otro modelo de objeto o numerosas clases contenedoras en el lado del cliente.

También recuerde que los métodos de extensión se agregaron como una forma de ayudar a que la consulta de Linq sea más legible, cuando se usa en su estilo de C #.

Estas 2 afectaciones son absolutamente equivalentes, sin embargo, la primera es mucho más legible (y la brecha en la legibilidad, por supuesto, boostía con más métodos encadenados).

 int n1 = new List {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List {1,2,3}, i => i % 2 != 0)); 

Tenga en cuenta que la syntax totalmente calificada debe ser:

 int n1 = new List {1,2,3}.Where(i => i % 2 != 0).Last(); int n2 = Enumerable.Last(Enumerable.Where(new List {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 pueden ser inferidos gracias a la presencia del primer parámetro de estos dos métodos (el parámetro que es introducido por la palabra clave this y hacerlos métodos de extensión). )

Evidentemente, este punto es una ventaja (entre otros) de los métodos de extensión, y puede aprovecharlo 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é para 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 (bien, este escenario es totalmente cursi): después de una buena noche, un animal abre los ojos y luego da un grito; cada animal abre los ojos de la misma manera, mientras que un perro ladra y un pato se despierta.

 public abstract class Animal { //some code common to all animals } public static class AnimalExtension { public static TAnimal OpenTheEyes(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 } } 

Conceptualmente OpenTheEyes debería ser un método Animal , pero luego devolvería una instancia de la clase abstracta Animal , que no conoce métodos de subclases específicos como Bark o Duck o lo que sea. Las 2 líneas comentadas como * 1 y * 2 generarían un error de comstackción.

Pero gracias a los métodos de extensión, podemos tener un tipo 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 { //some code common to all animals public TAnimal OpenTheEyes() 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 } } 

Esta vez, no hay parámetro y, por lo tanto, no hay posible inferencia de tipo de retorno. La llamada no puede ser más que:

 Goofy.OpenTheEyes().Bark(); Donald.OpenTheEyes().Kwak(); 

… que puede pesar mucho el código si está involucrado más encadenamiento (especialmente sabiendo que el parámetro de tipo siempre será en la línea de Goofy y en el de Donald …)

Solo tengo una palabra para contar al respecto: MANTENIMIENTO esta es la clave para el uso de métodos de extensión

Creo que los métodos de extensión ayudan a escribir código que es más claro.

En lugar de poner un nuevo método dentro de tu clase, como sugirió tu amigo, lo pones en el espacio de nombres ExtensionMethods. De esta manera, mantienes un orden lógico de orden en tu clase. Los métodos que no se relacionan directamente con su clase no lo saturarán.

Creo que los métodos de extensión hacen que tu código sea más claro y esté más organizado.

Le permite a su editor / IDE completar automáticamente la sugerencia.

Los amo por construir html. Con frecuencia hay secciones que se usan repetidamente, o se generan recursivamente cuando una función es útil, pero de lo contrario se interrumpiría el flujo del progtwig.

  HTML_Out.Append("
    "); foreach (var i in items) if (i.Description != "") { HTML_Out.Append("
  • ") .AppendAnchor(new string[]{ urlRoot, i.Description_Norm }, i.Description) .Append("
    ") .AppendImage(iconDir, i.Icon, i.Description) .Append(i.Categories.ToHTML(i.Description_Norm, urlRoot)).Append("
  • "); } return HTML_Out.Append("
").ToString();

También hay situaciones en las que un objeto necesita una lógica personalizada para prepararse para la salida HTML: los métodos de extensión le permiten agregar esta funcionalidad sin mezclar la presentación y la lógica dentro de la clase.

He encontrado que los métodos de extensión son útiles para hacer coincidir los argumentos generics nesteds.

Eso suena un poco extraño, pero digamos que tenemos una clase genérica MyGenericClass , y sabemos que TList es genérico (p. Ej., Una List ), no creo que haya una forma de desenterrar eso nested ‘ T ‘de la lista sin métodos de extensión ni métodos estáticos de ayuda. Si solo tenemos métodos estáticos de ayuda a nuestra disposición, es (a) feo, y (b) nos obligará a mover la funcionalidad que pertenece a la clase a una ubicación externa.

por ejemplo, para recuperar los tipos en una tupla y convertirlos en una firma de método, podemos usar métodos de extensión:

 public class Tuple { } public class Tuple : Tuple { } public class Tuple : Tuple { } public class Caller where TTuple : Tuple { /* ... */ } public static class CallerExtensions { public static void Call(this Caller> caller, T0 p0) { /* ... */ } public static void Call(this Caller> caller, T0 p0, T1 p1) { /* ... */ } } new Caller>().Call(10); new Caller>().Call("Hello", 10); 

Dicho esto, no estoy seguro de dónde debería estar la línea divisoria: ¿cuándo debería ser un método un método de extensión, y cuándo debería ser un método de ayuda estático? ¿Alguna idea?

Los métodos de extensión se pueden usar para crear un tipo de mixin en C #.

Esto, a su vez, proporciona una mejor separación de preocupaciones para conceptos ortogonales. Mire esta respuesta como un ejemplo.

Esto también se puede usar para habilitar roles en C #, un concepto central para la architecture DCI .

Tengo zonas de entrada en mi pantalla, y todas deben implementar un comportamiento estándar independientemente de sus tipos exactos (cuadros de texto, casillas de verificación, etc.). No pueden heredar una clase base común ya que cada tipo de zona de entrada ya se deriva de una clase específica (TextInputBox, etc.)

Quizás al subir a la jerarquía de herencia podría encontrar un ancestro común como, por ejemplo, WebControl, pero no desarrollé la clase de marco WebControl y no expone lo que necesito.

Con el método de extensión, puedo:

1) extender la clase WebControl y luego obtener mi comportamiento estándar unificado en todas mis clases de entrada

2) Como alternativa, todas mis clases se derivan de una interfaz, digamos IInputZone, y ampliar esta interfaz con métodos. Ahora podré llamar a los métodos de extensiones relacionados con la interfaz en todas mis zonas de entrada. Logré así un tipo de herencia múltiple ya que mis zonas de entrada ya derivaban de múltiples clases base.

Hay tantos ejemplos excelentes de métodos de extensión … especialmente en IEnumerables como se publicó anteriormente.

por ejemplo, si tengo un IEnumerable , puedo crear y un método de extensión para IEnumerable

 mylist List; 

… crea la lista

 mylist.DisplayInMyWay(); 

Sin los métodos de extensión tendrían que llamar:

 myDisplayMethod(myOldArray); // can create more nexted brackets. 

otro gran ejemplo es la creación de una lista circular enlazada en un instante.

¡Puedo ‘tomarme el crédito por eso!

lista vinculada circular utilizando métodos de extensión

Ahora combine estos y use el código de métodos de extensión para leer de la siguiente manera.

 myNode.NextOrFirst().DisplayInMyWay(); 

más bien que

 DisplayInMyWay(NextOrFirst(myNode)). 

usando métodos de extensión Es más limpio y fácil de leer y más orientado a objetos. también muy cerca de:

 myNode.Next.DoSomething() 

¡Muéstrale eso a tu colega! 🙂