2009-09-20 11 views
52

Quiero reemplazar los métodos de extensión incluidos en .NET o ASP MVC por mis propios métodos.Cómo anular un método de extensión existente

Ejemplo

public static string TextBox(this HtmlHelper htmlHelper, string name) 
{ 
    ... 
} 

¿Es posible? No puedo usar la anulación o la nueva palabra clave.

Respuesta

93

ACTUALIZACIÓN: Esta pregunta era the subject of my blog in December of 2013. Gracias por la gran pregunta!


Puede hacer esto, en cierto sentido. Pero debería comenzar hablando brevemente sobre el principio de diseño básico de la resolución de sobrecarga en C#. La resolución de sobrecarga es, por supuesto, tomar un conjunto de métodos con el mismo nombre y elegir el mejor miembro único para llamar.

Hay muchos factores involucrados en determinar cuál es el "mejor" método; diferentes idiomas usan una "mezcla" de factores diferente para resolver esto. C# en particular pesa mucho la "cercanía" de un método dado al sitio de llamadas. Si se le da la opción entre un método aplicable en una clase base o un nuevo método aplicable en una clase derivada, C# toma uno en la clase derivada porque está más cerca, incluso si el de la clase base es de otra manera un mejor partido.

Y así que corremos la lista. Las clases derivadas están más cerca que las clases base. Las clases internas están más cerca que las clases externas. Los métodos en la jerarquía de clases son más cercanos que los métodos de extensión.

Y ahora llegamos a su pregunta.La cercanía de un método de extensión depende de (1) ¿cuántos espacios de nombres hay que "salir"? y (2) ¿encontramos el método de extensión a través del using o estaba allí mismo en el espacio de nombres? Por lo tanto, puede influir en la resolución de sobrecarga cambiando en qué espacio de nombres aparece su clase de extensión estática, para ponerlo en un espacio de nombre más cercano al sitio de llamadas. O bien, puede cambiar sus declaraciones using, para poner el using del espacio de nombres que contiene la clase estática deseada más cercana que la otra.

Por ejemplo, si usted tiene

namespace FrobCo.Blorble 
{ 
    using BazCo.TheirExtensionNamespace; 
    using FrobCo.MyExtensionNamespace; 
    ... some extension method call 
} 

entonces es ambiguo, que está más cerca. Si desea dar prioridad a la suya sobre la suya, se puede optar por hacer esto:

namespace FrobCo 
{ 
    using BazCo.TheirExtensionNamespace; 
    namespace Blorble 
    { 
    using FrobCo.MyExtensionNamespace; 
    ... some extension method call 
    } 

Y ahora cuando la resolución de sobrecarga va a resolver el método de extensión de llamada, las clases en Blorple conseguir el primer tanto, id, clases en FrobCo.MyExtensionNamespace, luego clases en FrobCo, y luego las clases en BazCo.TheirExtensionNamespace.

¿Está claro?

+0

una pregunta aquí, ¿cómo puedo ocultar mi método de clase sobre el método de extensión creado para mi clase? p.ej. Class Deer tiene el método Run y ​​quiero asegurarme de que cuando use Deer dentro del espacio de nombres "Mars" (clase MarsLand) me asegure de que se invoque el método Run declarado en los métodos de extensión del espacio de nombres Mars. El ciervo es parte del espacio de nombres de la Tierra. – Dhananjay

+3

@Dhananjay: un método aplicable dentro de la jerarquía de clases * always * gana sobre un método de extensión. Si necesita llamar a un método de extensión, llámelo como un método estático. –

+1

¿Por qué 'FrobCo.MyExtensionNamespace' * no * está más cerca de cualquier clase en' FrobCo.Blorble' que 'BazCo.TheirExtensionNamespace'? Me parece que para las extensiones de BazCo necesitaría subir dos niveles de espacio de nombres y luego bajar otros dos, mientras que para las extensiones de FrobCo solo necesitaría subir y bajar un nivel de espacio de nombres. Por lo tanto, para mí, las extensiones de FrobCo parecen más cercanas. – Virtlink

26

Los métodos de extensión no pueden anularse porque no son métodos de instancia y no son virtuales.

El compilador se quejará si se importan ambas clases de método de extensión a través del espacio de nombres ya que no se sabe qué método para llamar:

La llamada es ambigua entre los métodos o propiedades siguientes: ...

La única forma de evitar esto es llamar a su método de extensión utilizando la sintaxis del método estático normal. Así que en lugar de esto:

a.Foo(); 

que tendría que hacer esto:

YourExtensionMethodClass.Foo(a); 
+0

bien como Eric señala que no es la importación de dos métodos de extensión coincidente diferentes, son ellos la importación en el mismo nivel de anidamiento. y estoy seguro de que está hablando de anular el comportamiento de uno método con el comportamiento de un comportamiento diferente y no polimórfico –

4

métodos de extensión son básicamente métodos simplemente estáticos, así que no sé cómo se los puede sustituir, pero si usted acaba de poner en un espacio de nombre diferente, entonces puede llamar al suyo en lugar del que desea reemplazar.

Pero Matt Manela habla de cómo los métodos de instancia tienen prioridad sobre los métodos de extensión: http://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/e42f1511-39e7-4fed-9e56-0cc19c00d33d

Para más ideas sobre los métodos de extensión se puede ver en http://gen5.info/q/2008/07/03/extension-methods-nulls-namespaces-and-precedence-in-c/

Editar: Me olvidé del tema ambigüedad, por lo que su La mejor opción es intentar no incluir los métodos de extensión que desea reemplazar. Por lo tanto, es posible que no necesite utilizar la directiva 'using', sino que simplemente incluya todo el nombre del paquete de algunas clases, y esto podría resolver el problema.

3

Sobre la base de Eric premisa (y el hecho de que las opiniones de código es traducido al espacio de nombres ASP) que debe ser capaz de anularlo como esto (al menos a mí me funciona en ASP.NET MVC4.0 Razor

using System.Web.Mvc; 

namespace ASP { 
    public static class InputExtensionsOverride { 
    public static MvcHtmlString TextBox(this HtmlHelper htmlHelper, string name) { 
     TagBuilder tagBuilder = new TagBuilder("input"); 
     tagBuilder.Attributes.Add("type", "text"); 
     tagBuilder.Attributes.Add("name", name); 
     tagBuilder.Attributes.Add("crazy-override", "true"); 
     return new MvcHtmlString(tagBuilder.ToString(TagRenderMode.Normal)); 
    } 
    } 
} 

Nota el espacio de nombres tiene que ser "ASP".

Cuestiones relacionadas