2009-01-23 9 views
24

He estado pensando en usar métodos de extensión como reemplazo de una clase base abstracta. Los métodos de extensión pueden proporcionar funcionalidad predeterminada, y pueden "anularse" al poner un método de la misma firma en una clase derivada.Reemplazando los métodos de extensión

¿Alguna razón por la que no debería hacer esto?

Además, si tengo dos métodos de extensión con la misma firma, ¿cuál se usa? ¿Hay alguna forma de establecer prioridad?

+2

Votando porque creo que es una buena pregunta, pero una mala idea. –

Respuesta

3

Estoy de acuerdo con Michael. Las clases base deben contener toda la funcionalidad básica. Los métodos de extensión deberían, obviamente, extender la funcionalidad básica. En los lenguajes dinámicos como Ruby, a menudo es típico usar métodos de extensión para proporcionar funcionalidad adicional en lugar de usar subclases. Básicamente, los métodos de extensión están ahí para reemplazar el uso de subclases, no para reemplazar el uso de clases base.

La única excepción a esto que he visto es que si tiene múltiples tipos que tienen jerarquías de clases diferentes (como controles de winform), puede crear una subclase de cada una que implemente e interfiera y luego extender esa interfaz, por lo tanto dando funcionalidad "base" a un grupo de controles diferentes, sin extender todo como Control u Objeto.

Editar: responder a la segunda pregunta

creo que el compilador detectará esto para usted.

+0

Cuando dices que los métodos de extensión son para reemplazar subclases, no clases base, no estoy seguro de a qué te refieres. Dado que el compilador prefiere la implementación local sobre la implementación de la extensión, ¿los métodos de extensión no son métodos 'base'? – JoshRivers

+0

Lo que quiero decir es que normalmente si usabas un ensamblado con una clase Foo y necesitabas agregar un método DoSomething, harías una subclase SubFoo que tuviera un método DoSomething. Con los métodos de extensión, puede reemplazar la necesidad de subclases haciendo un método de extensión. –

12

En general, no debe proporcionar la funcionalidad "base" a través de métodos de extensión. Solo deben usarse para "extender" la funcionalidad de la clase. Si tiene acceso al código de clase base, y la funcionalidad que está tratando de implementar es lógicamente parte de la jerarquía de herencia, entonces debe ponerlo en la clase abstracta.

Mi punto es, sólo porque usted puede no se debe significar. A menudo es mejor seguir con un buen OOP anticuado y usar las características del lenguaje más nuevo cuando la programación anterior de OO no es suficiente para proporcionarle una solución razonable.

2

Son operaciones semánticamente diferentes. Por ejemplo, el polimorfismo puede no funcionar de la forma en que lo haría con una clase base abstracta.

Como regla general, utilice cualquier herramienta de lenguaje para lo que está diseñado. Los métodos de extensión no reemplazan la herencia, son una técnica para extender la funcionalidad de una clase utilizando (normalmente) su interfaz ya visible.

2

Hay muchas razones por las que no deberías hacerlo. La primera de ellas es que no se puede garantizar cómo se llaman sus extensiones:

MyExtensions.AMethod(myObj) 

o

myObj.AMethod() 

El segundo es el azúcar meramente sintáctica para la primera.

Lo que sugiere va en contra del espíritu de la función de idioma. Los métodos de extensión son decididamente no orientados a objetos. Sin embargo, estás tratando de lograr una técnica orientada a objetos. No use métodos de extensión para eso.

+0

¿Por qué es importante la sintaxis de ejecución? ¿No es 'el espíritu de la característica' un argumento filosófico? Los principios SOLIDOS no siempre están "orientados a objetos". ¿A veces necesitas violar las 'reglas' para obtener un buen diseño? – JoshRivers

+0

Lo que estás sugiriendo es un truco y volverá para morderte la parte trasera. Estás luchando contra el lenguaje. Estás tratando de escalar la cuerda floja del éxito y caminar sobre el pozo del fracaso. Por favor no hagas eso. – yfeldblum

8

Esto definitivamente es una mala idea.Los métodos de extensión están vinculados estáticamente, lo que significa que, a menos que invoque la anulación de un objeto cuyo tipo de tiempo de compilación es el subtipo, seguirá llamando al método de extensión. Bye-bye polimorfismo. This page tiene una buena discusión de los peligros de los métodos de extensión.

+0

Sí, esto es un problema. Traté de agregar un método de extensión a Control para proporcionar una implementación predeterminada de un nuevo método, y luego planeé subclasificar algunos controles donde quería proporcionar un comportamiento específico. Una referencia de clase base (a Control) no invoca las versiones de subtipo. Esperaba que el método "real" siempre prevalecería sobre el método de extensión, incluso en un subtipo, pero este no es el caso. Exactamente como dijiste –

+0

Sin embargo, pude solucionar esto bastante bien. Dado que los métodos que agrego a los controles subclasificados pertenecen a una nueva interfaz personalizada, el código del cliente puede primero intentar enviar una instancia de Control a la nueva interfaz, y si la interfaz no es compatible, llame al método de extensión. –

4

Todas las respuestas aquí indicaron "no se puede", lo cual es cierto hasta donde llega. La mayoría agregó "y no deberías". Me gustaría argumentar que usted debería poder: una pequeña comodidad ya que puede ser.

Tome un doloroso ejemplo del mundo real: si tiene la mala suerte de estar utilizando el nuevo MVC framework, y su código de vista está usando algún método de extensión HtmlHelper por todas partes, y desea anular su comportamiento predeterminado ... ¿entonces que?

Eres SOL, eso es. Incluso si realizó la "OOP thing" - derivada de HtmlHelper, cambie su clase de vista base para reemplazar la instancia de objeto 'Html' con una instancia de DerivedHtmlHelper, y defina un método 'Foo' explícito en ella, incluso si lo hizo todo que, al llamar a 'Html.Foo', todavía invocará el método de extensión original y no el método.

¡Esto es sorprendente! ¡Después de todo, solo se espera aplicar los métodos de extensión si el objeto aún no tiene un método! ¿Que está pasando aqui?

Bueno, esto porque los métodos de extensión son función estática. Es decir, al ver 'Html.Foo', el compilador examina el tipo static de 'Html'. Si tiene un método 'Foo', se llama como de costumbre. De lo contrario, si hay 'SomeClass' que proporciona un método de extensión 'Foo', convierte la expresión a 'SomeClass.Foo (Html)'.

Lo que cabría esperar es que el compilador considere el tipo de objeto dinámico del objeto. Es decir, que el código generado (pseudo-) leería 'Html.HasMethod (' Foo ')? Html.Foo(): SomeClass.Foo (Html) '.

Esto, por supuesto, incurriría en el costo de usar la reflexión en cada llamada al método de extensión. Por lo tanto, es de esperar que pueda escribir algo como 'static void Foo (virtual este HtmlHelper html)' para solicitar explícitamente al compilador que inserte la comprobación en tiempo de ejecución. Llamar a esto un "método de extensión virtual".

Sin embargo, en su presupuesto limitado y sabiduría infinita, los diseñadores del lenguaje C# fueron con la alternativa más eficiente y más restringida. Lo cual me deja todavía SOL cuando necesito anular el comportamiento predeterminado de HtmlHelper :-(

0

Primero sería bueno marcar [nuevo] como un modificador de método - debería dar la misma separación de perf y método pero con mucho menos de una molestia

Si después de que usted está todavía está ponderando los mismos trucos, aquí están las opciones principales a tener en cuenta -. involucrada ninguna ideología, simplemente lista apsects que tendrá que tener en cuenta :-)

Para empezar, puede que tenga que limitar todos los usos a los parámetros de función con plantilla para garantizar la selección determinística de funciones, ya que este truco creará la situación que es exactamente inversa a la de la CLR guar anticipa la prioridad de enlace determinista a través de los límites de llamada de función.

Si todavía va a tener una clase base abstracta y posee todo el código, simplemente "convertir" un método base en extensión no le compraremos nada y le costará la pérdida de una interfaz común que seguirá existen para todo lo demás, incluido el costo de vtables. Si desea convertir clases abstractas en concretas, eliminando todos los métodos virtuales por razones de rendimiento y utilizando clases base de concreto y todos los derivados exclusivamente en plantillas (tal vez incluso convirtiendo a estructuras), entonces ese truco podría comprarle alguna perforación . Solo debe tener en cuenta que no podrá volver al uso basado en interfaz sin refactorizar. También deberá comprobar el orden de invocación en funciones que no sean de plantilla con la clase base en la firma y tener mucho cuidado si tiene varias DLL-S. Pruebe lo mismo con el operador [nuevo] y vea si funciona mejor.

Además, tendrá que escribir la implementación de métodos por separado para cada clase derivada, incluso si tienen exactamente la misma funcionalidad, por lo que si tiene muchas clases derivadas está buscando mucha duplicación de código. Esta parte te afectará incluso si usas [nuevo] a menos que vuelvas a los métodos virtuales e introduzcas otra capa en la herencia, lo que cancelará todas las ganancias de rendimiento, por supuesto.

2

Es posible que desee considerar lo anterior, el polimorfismo si no necesita clases de derivados únicas.

algunas pautas generales de esta MSDN: article

  1. Una vez que el método se define dentro de una clase, cualquier método de extensión para que el compartimiento de clases que ya no será ejecutado nombre del método.

  2. Los métodos de extensión se cargan por espacio de nombres. Para permitir al usuario de otros métodos de extensión, probablemente debería evitar poner los métodos de extensión en el mismo espacio de nombres que la clase que se está ampliando.

Si varios ensamblados dependientes están consumiendo la misma biblioteca, cada uno requiere una función personalizada contra una clase en la biblioteca, se puede declarar la extensión específica para el ensamblado dependiente en un espacio de nombres dentro de ese montaje. Otros ensamblajes que no dependen de ese ensamblaje no tendrán esa extensión.

Si bien esto no es polimorfismo, es posible que no desee utilizar el polimorfismo, ya que se requerirían todas las clases derivadas para implementar sus reemplazos.

En otras palabras, si tiene derivados con lógica variante específica, use polimorfismo. Si simplemente necesita funciones personalizadas en casos específicos que de otra manera podrían ser confusos o gravosos, los métodos de extensión no son una mala idea.

También, vea la respuesta de JoshRivers mostrando cómo anular un método de extensión usando un espacio de nombres dentro de una clase dentro de un espacio de nombres separado.

Cuestiones relacionadas