2010-03-26 7 views
11

Los considero una forma muy natural de extender las clases existentes, especialmente cuando solo necesita "soldar por puntos" alguna funcionalidad en una clase existente.¿Cuál es la motivación detrás de "Usar los métodos de extensión con moderación?"

Microsoft dice: "En general, recomendamos implementar métodos de extensión con moderación y solo cuando sea necesario". Y sin embargo, los métodos de extensión forman la base de Linq; de hecho, Linq fue la razón por la que se crearon los métodos de extensión.

¿Existen criterios de diseño específicos donde se usen métodos de extensión sobre la herencia o la composición? ¿Bajo qué criterio están desanimados?

Respuesta

17

Mostramos las nuevas funciones de lenguaje propuestas (al menos las que tienen una posibilidad medianamente decente de ver la luz del día) a los MVP C# para una retroalimentación temprana. Algunos comentarios que recibimos muy a menudo sobre muchas características es "bien I usaría esta función juiciosamente y de acuerdo con los principios de diseño de la función, pero mis compañeros de trabajo se van a volver loco con esto y escriben un cantidad de código inmanejable y no comprensible que me voy a encargar de depurar durante los próximos diez años, así que por mucho que lo desee, ¡nunca implemente esta función! "

Aunque estoy exagerando un tanto por el efecto cómico, esto es algo que tomamos muy en serio; Queremos que el diseño de nuevas funciones para fomente su uso, pero desalentar su uso indebido.

Nos preocupaba que los métodos de extensión se utilizasen para, por ejemplo, extender indiscriminadamente el "objeto" de forma arbitraria y crear así grandes bolas de lodo tieso que sería difícil de entender, depurar y mantener.

Personalmente estoy particularmente preocupado por los métodos de extensión "lindos" que se ven en la programación "fluida". Cosas como

6.Times(x=>Print("hello")); 

Yuck. los bucles "for" son universalmente comprensibles e idiomáticos en C#; no te pongas lindo.

+0

Tan irónico como este es, ese método de extensión podría colocarse justo al lado de mi método '.ForEach '! Me parece fácil de entender y mucho menos detallado que el bucle for, también sería imposible introducir accidentalmente el error off por 1 usando i

+2

@Chris: En ese caso, solo usa foreach. La excelente publicación de Eric aquí explica los problemas con ForEach - http://blogs.msdn.com/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx –

+2

Y, sin embargo, F # incluye 'Seq.iter' que es exactamente equivalente a un método de extensión 'IEnumerable .ForEach'. Conozco un lenguaje diferente, una filosofía de diseño diferente, pero el argumento sobre la producción de efectos secundarios aún podría aplicarse, y F # está mucho más cerca de ser funcionalmente "puro" que C#. –

11

El problema es que los métodos de extensión no son necesariamente claros.

Cuando vea algo como esto en código:

myObject.Foo(); 

El instinto natural es que Foo() es un método definido en la clase de myObject, o una clase padre de la clase de myObject.

Con los métodos de extensión, eso simplemente no es cierto. El método podría definirse casi en CUALQUIER LUGAR, para casi cualquier tipo (puede definir un método de extensión en System.Object, aunque es una mala idea ...). Esto realmente aumenta el costo de mantenimiento de su código, ya que reduce la detectabilidad.

Cada vez que agrega un código que no es obvio, o incluso reduce la "obviedad" de la implementación, hay un costo involucrado en términos de mantenibilidad a largo plazo.

+2

¿Es tan difícil hacer clic derecho y seleccionar "Ir a definición"? –

+2

@Mongus: No, no es difícil. - pero es probable que todavía se equivoque sobre dónde se define en la jerarquía. Además, si tiene métodos de extensión con el mismo nombre que los métodos de instancia, agrega confusión. Hay muchas razones para evitarlos, incluso si a menudo son útiles. –

+0

Al menos mientras tipea, el intellisense proporciona indicadores a través de iconos en el menú de intellisense sobre el tipo de función. – RBT

2

Cuando Borland los inventó (Delphi Class Helpers), la guía ya era: "debe usarse solo en situaciones excepcionales".

Se han vuelto mucho más aceptados (como parte de LINQ), pero siguen siendo una forma extraña de agregar funcionalidad. Creo que Reed tiene una buena respuesta sobre por qué.

6

El uso excesivo de métodos de extensión es malo por la misma razón que el uso excesivo de operadores sobrecargados es malo. Déjame explicarte.

Primero veamos el ejemplo de sobrecarga del operador. Cuando vea el código:

var result = myFoo + myBar; 

que le gustaría esperar que myFoo y myBar son tipos numéricos, y la operator + es la realización de adición. Esto es lógico, intuitivo, fácil de entender. Pero una vez que la sobrecarga del operador entra en escena, ya no puede estar seguro de lo que myFoo + myBar está realmente haciendo - el operador + podría estar sobrecargado en significar cualquier cosa. No puede simplemente leer el código y descubrir qué sucede sin tener que leer todo el código subyacente a los tipos implicados en la expresión. Ahora, operator + ya está sobrecargado para tipos como String o DateTime - sin embargo, hay una interpretación intuitiva de lo que significa Además en esos casos. Más importante aún, en esos casos comunes, agrega mucho poder expresivo al código. Por lo tanto, vale la pena la posible confusión.

Entonces, ¿qué tiene que ver todo esto con los métodos de extensión? Bueno, los métodos de extensión introducen una situación similar.Sin métodos de extensión, cuando vean:

var result = myFoo.DoSomething(); 

se puede asumir que DoSomething() es o bien un método de myFoo o uno de ellos es las clases base. Esto es simple, fácil de entender, incluso intuitivo. Pero con los métodos de extensión, DoSomething()podría definirse en cualquier lugar - y peor, la definición depende del conjunto de declaraciones using en el archivo de código y traer potencialmente muchas clases a la mezcla, cualquiera de las cuales podría alojar la implementación de DoSomething().

Ahora no me malinterprete. Tanto la sobrecarga del operador como los métodos de extensión son funciones de lenguaje útiles y potentes. Pero recuerde: con gran potencia viene con una gran responsabilidad. Debe usar estas características cuando mejoran la claridad o la capacidad de la implementación. Si comienzas a usarlos indiscriminadamente, agregarán confusión, complejidad y posiblemente defectos a lo que intentas crear.

+2

+1 por citar a Uncle Ben. – RationalGeek

+0

'Pero con los métodos de extensión, DoSomething() podría definirse en cualquier lugar, y lo que es peor, la definición depende del conjunto de instrucciones using en el archivo de código y traer potencialmente muchas clases a la mezcla, cualquiera de las cuales podría alojar la implementación de DoSomething() '- Uno simplemente necesita hacer clic derecho y seleccionar ir a su definición desde el menú contextual para resolver esto (en Visual Studio). – RBT

1

Los métodos de extensión se pueden considerar como Orientado a aspectos. Creo que Microsoft está diciendo que, si tienes el código fuente, deberías estar alterando la clase original. Tiene que ver con la claridad y la facilidad de mantenimiento. La razón por la cual se necesitaron métodos de extensión es porque extienden la funcionalidad de los objetos existentes. En el caso de LINQ, si profundiza en cómo funciona, los objetos se pueden crear con una variedad de tipos que no se pueden conocer antes de tiempo. El uso de métodos de extensión permite agregar algunos comportamientos.

Por ejemplo, tiene sentido ampliar los objetos de .NET framework porque no tiene el origen, pero puede tener algún comportamiento para agregar a un objeto. Extendiendo la cadena, las clases DateTime y FrameworkElement son aceptables. También puede ser aceptable crear métodos de extensión para las clases que cruzan los límites de la aplicación. Digamos, si quisieras compartir una clase de DTO y tener algún comportamiento de UI específico, crea ese método de extensión solo en la IU.

Con lenguajes dinámicos, el paradigma es diferente, pero creo que estás hablando de C# y VB.net.

3

Sigo una regla muy simple con métodos de extensión y clases de ayuda.

Cada vez que tengo un método que podría ser relegado a una clase de ayuda estática voy a sopesar la utilidad de este en un alcance general, ¿realmente quiero que este método se comparta? ¿El significado es claro y útil para otros?

Si puedo responder afirmativamente a esta pregunta, la crearé para que sea un método de extensión. Donde tengo una biblioteca compartida que, entre otras cosas, contiene todos mis métodos de extensión en 1 espacio de nombres con cada archivo de clase definido como TypeNameExtension, por lo que queda muy claro qué esperar dentro de esa clase para que pueda ubicar el código fácilmente.

Si cuestiono la necesidad de un método auxiliar como un uso accesible a nivel mundial, declararé el método como privado estático y lo dejaré dentro de la clase propietaria.

2

Tengo una creciente biblioteca de métodos de extensión que operan en clases BCL para simplificar problemas comunes, que a primera vista parecen confusos. Francamente, creo que los métodos de extensión hacen que las cosas sean mucho más legibles. Por ejemplo, tomemos una declaración si la prueba para ver si un número está entre dos números:

¿Qué pasa si right es menor que left el accidente? ¿Y exactamente qué hace eso? Cuando las variables son simplemente como x y left, entonces es fácil de entender. Pero, ¿y si son propiedades en una clase larga? La declaración if puede ser bastante grande, lo que ofusca lo más simple que estás tratando de hacer.

Así que escribió esto:

public static bool Between<T>(this T test, T minValue, T maxValue) 
    where T : IComparable<T> 
{ 
    // If minValue is greater than maxValue, simply reverse the comparision. 
    if (minValue.CompareTo(maxValue) == 1) 
     return (test.CompareTo(maxValue) >= 0 && test.CompareTo(minValue) <= 0); 
    else 
     return (test.CompareTo(minValue) >= 0 && test.CompareTo(maxValue) <= 0); 
} 

Ahora puedo mejorar mi sentencia if en esto:

if (x.Between(left, right)) 
{ 
    // Do Something 
} 

Es esta "extraordinaria"? No lo creo ... pero sí creo que es una mejora significativa en la legibilidad, y permite cierta verificación adicional en las entradas de prueba para mayor seguridad.

El peligro que creo que Microsoft quiere evitar aquí es que la gente escriba un montón de métodos de extensión que redefinen Equals y ToString.

+0

¿Pero es realmente mejor que si (Entre (prueba, minv, maxv)) {}? – jmucchiello

+0

@jmucchiello - En realidad, sería 'if (MathUtilities.Between (test, minv, maxv)) {}'. Entonces, usted debería preguntarse, ¿qué valor me dio tener 'MathUtilities', y lo quitó de la legibilidad? Creo que la respuesta es sí. Ese es todo el punto detrás de las interfaces Fluent. – Nick

+0

@jmucchiello ciertamente lee mejor y es más claro en cuanto a lo que va a ser entre ... –

Cuestiones relacionadas