2009-09-20 22 views
52

Ich möchte die im .NET- oder ASP MVC-Framework enthaltenen Erweiterungsmethoden durch meine eigenen Methoden ersetzen.So überschreiben Sie eine vorhandene Erweiterungsmethode

Beispiel

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

Ist es möglich? Ich kann das override- oder new-Schlüsselwort nicht verwenden.

Antwort

93

UPDATE: Diese Frage war the subject of my blog in December of 2013. Danke für die tolle Frage!


Sie können dies in gewissem Sinne tun. Aber ich sollte damit anfangen, kurz über das grundlegende Konstruktionsprinzip der Überladungsauflösung in C# zu sprechen. Bei der Überladungsauflösung geht es natürlich darum, eine Reihe von Methoden mit dem gleichen Namen zu verwenden und aus dieser Gruppe das eindeutig beste Mitglied auszuwählen.

Es gibt viele Faktoren bei der Bestimmung, welche die "beste" Methode ist; verschiedene Sprachen verwenden eine andere "Mischung" von Faktoren, um dies herauszufinden. C# gewichtet insbesondere die "Nähe" einer gegebenen Methode zur Call-Site stark. Wenn die Wahl zwischen einer anwendbaren Methode in einer Basisklasse oder einer neuen anwendbaren Methode in einer abgeleiteten Klasse gegeben ist, nimmt C# die eine in der abgeleiteten Klasse, weil sie näher ist, auch wenn die in der Basisklasse auf andere Weise besser ist Spiel.

Und so laufen wir die Liste herunter. Abgeleitete Klassen sind näher als Basisklassen. Innere Klassen sind näher als äußere Klassen. Methoden in der Klassenhierarchie sind näher als Erweiterungsmethoden.

Und jetzt kommen wir zu Ihrer Frage.Die Nähe einer Erweiterungsmethode hängt ab von (1) wie viele Namespaces "out" mussten wir gehen? und (2) haben wir die Erweiterungsmethode über using gefunden oder war es genau dort im Namensraum? Daher können Sie die Überladungsauflösung beeinflussen, indem Sie ändern, in welchem ​​Namespace Ihre statische Erweiterungsklasse angezeigt wird, um sie in einen näheren Namespace für die Aufrufsite einzufügen. Oder Sie können Ihre using Deklarationen ändern, um den using des Namespace, der die gewünschte statische Klasse enthält, näher als die anderen zu setzen.

Zum Beispiel, wenn Sie

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

haben, dann ist es nicht eindeutig, welche näher ist. Wenn Sie Ihr über ihre Prioritäten zu setzen möchten, können Sie sich entscheiden, dies zu tun:

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

Und nun, wenn eine Überlastung Auflösung der Erweiterung Methodenaufruf zur Lösung geht, Klassen in Blorple zuerst sich gehen, dann Klassen in FrobCo.MyExtensionNamespace, dann Klassen in FrobCo, und dann Klassen in BazCo.TheirExtensionNamespace.

Ist das klar?

+0

eine Frage hier, wie kann ich meine Klassenmethode vor der Erweiterungsmethode verbergen, die für meine Klasse erstellt wurde? z.B. Class Deer hat Run-Methode und ich möchte sicherstellen, wenn ich Deer innerhalb "Mars" Namespace (MarsLand Klasse) verwenden, dann möchte ich sicherstellen, dass Run-Methode in Erweiterung Methoden des Mars-Namespace deklariert werden aufgerufen. Deer ist Teil des Namensraums Erde. – Dhananjay

+3

@Dhananjay: Eine anwendbare Methode innerhalb der Klassenhierarchie * always * gewinnt über eine Erweiterungsmethode. Wenn Sie eine Erweiterungsmethode zum Aufrufen benötigen, rufen Sie sie als statische Methode auf. –

+1

Warum ist 'FrobCo.MyExtensionNamespace' * nicht näher an irgendeiner Klasse in' FrobCo.Blorble' als 'BazCo.TheirExtensionNamespace'? Mir scheint, dass Sie für die BazCo-Erweiterungen zwei Namespace-Ebenen und dann zwei andere nach oben gehen müssen, während Sie für FrobCo-Erweiterungen nur eine Namespace-Ebene nach unten gehen müssen. Daher scheinen mir die FrobCo-Erweiterungen näher zu sein. – Virtlink

26

Erweiterungsmethoden können nicht überschrieben werden, da sie keine Instanzmethoden sind und nicht virtuell sind.

Der Compiler wird sich beschweren, wenn Sie beide Erweiterungsmethode Klassen über Namespace importieren, da es die Methode aufrufen, wird nicht wissen: ...

:

Der Anruf zwischen den folgenden Methoden oder Eigenschaften nicht eindeutig ist

Der einzige Weg, um dies zu erreichen, ist Ihre Erweiterungsmethode mit der normalen statischen Methodensyntax aufzurufen. Anstatt also diese:

a.Foo(); 

Sie müssten dies tun:

YourExtensionMethodClass.Foo(a); 
+0

Nun, wie Eric merkt es nicht zwei verschiedene passende Erweiterungsmethoden ist importieren, ist es ihnen auf dem gleichen Niveau der Verschachtelung. und ich bin sicher, er spricht überschreiben das Verhalten eines Import Methode mit dem Verhalten eines anderen und nicht über polymorphes Verhalten –

4

Erweiterungsmethoden sind im Grunde nur statische Methoden, damit ich weiß nicht, wie man sie überschreiben können, aber wenn Sie nur setzen Sie können sie in einem anderen Namensraum aufrufen als den, den Sie ersetzen möchten.

Aber Matt Manela spricht darüber, wie Instanzmethoden Vorrang vor den Erweiterungsmethoden haben: http://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/e42f1511-39e7-4fed-9e56-0cc19c00d33d

Für weitere Ideen über Erweiterungsmethoden Sie bei http://gen5.info/q/2008/07/03/extension-methods-nulls-namespaces-and-precedence-in-c/

bearbeiten aussehen kann: ich über die Mehrdeutigkeit Problem vergessen, so dass Ihr Am besten versuchen Sie, die Erweiterungsmethoden, die Sie ersetzen möchten, nicht zu berücksichtigen. Daher müssen Sie möglicherweise nicht die Anweisung 'using' verwenden, sondern nur den gesamten Paketnamen einiger Klassen eingeben, um das Problem zu lösen.

3

Basierend auf Eric Prämisse (und die Tatsache, dass Code sieht in ASP-Namespace gerendert wird), sollten Sie es so außer Kraft setzen kann (zumindest funktioniert es für mich in 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)); 
    } 
    } 
} 

Hinweis hat der Namespace „ASP“ sein.

Verwandte Themen