Ich habe vor kurzem mit der Idee gespielt, Erweiterungs-Methoden zu verwenden, um Hilfsprogramme für Klassen zu implementieren, die ich kontrolliere (dh ich bin im selben Programm und kann modifizieren). Das Grundprinzip dahinter ist, dass diese Hilfsprogramme häufig in sehr spezifischen Szenarien verwendet werden und keinen Zugriff auf die internen Werte der Klassen benötigen.
Nehmen wir zum Beispiel an, ich hätte eine StackExchange-Klasse. Es hätte Methoden wie PostQuestion
und Search
und AnswerQuestion
.
Was nun, wenn ich meinen Ruf manuell berechnen möchte, um sicherzustellen, dass StackOverflow mich nicht betrügt. Ich würde etwas wie folgt implementieren:
%Vor%Ich könnte der StackExchange-Klasse eine Methode hinzufügen, benötigt jedoch keine Interna und wird nur von einem oder zwei anderen Teilen des Programms verwendet.
Stellen Sie sich vor, Sie hätten stattdessen 10 oder 20 dieser spezifischen Hilfsmethoden. Nützlich in einem bestimmten Szenario, aber sicher nicht für den allgemeinen Fall. Meine Idee verändert etwas wie
%Vor%Zu etwas wie
%Vor%Beachten Sie den Namespace. Ich würde das idealerweise verwenden, um Erweiterungsmethoden innerhalb eines bestimmten Szenarios zu gruppieren. Zum Beispiel "RepCalculations", "Statistics", etc.
Ich habe versucht zu suchen, ob dieser Mustertyp überhaupt gehört wird, und habe keinen Hinweis gefunden, dass Erweiterungsmethoden für irgendetwas außer Klassen verwendet werden, die Sie nicht ändern können.
Welche Mängel gibt es bei diesem "Muster"? Soll ich stattdessen bei der Vererbung oder Komposition bleiben oder nur eine gute alte statische Helferklasse?
Ich würde den Abschnitt der Framework Design Guidelines zu Extension-Methoden lesen. Hier ist ein Beitrag nach dem anderen der Autoren für die 2. Auflage. Der Anwendungsfall, den Sie beschreiben (Spezielle Hilfsmethoden) wird von Phil Haack als eine gültige Verwendung für Erweiterungsmethoden bezeichnet, mit dem Nachteil, dass zusätzliche Kenntnisse der API erforderlich sind, um diese "versteckten" Methoden zu finden.
In diesem Post nicht erwähnt, aber in diesem Buch empfohlen, gehen die Erweiterungsmethoden in einen separaten Namespace der erweiterten Klasse ein. Andernfalls werden sie immer mit Intellisense angezeigt und es gibt keine Möglichkeit, sie auszuschalten.
Ich denke, ich habe dieses Muster woanders gesehen. Es könnte ziemlich verwirrend, aber auch ziemlich mächtig sein. Auf diese Weise können Sie eine Klasse in einer Bibliothek und eine Reihe von Erweiterungsmethoden in einem separaten Namespace bereitstellen. Dann kann jeder, der Ihre Bibliothek verwendet, den Namespace mit Ihren Erweiterungsmethoden importieren oder eigene Erweiterungsmethoden bereitstellen.
Ein guter Kandidat für dieses Muster wäre, wenn Sie über Erweiterungsmethoden verfügen, die nur für Komponententests verwendet werden (z. B. um zu vergleichen, ob zwei Objekte in einem Sinn gleich sind, den Sie nur für Komponententests benötigen).
Sie scheinen den Vergleich zu machen, dass die Erweiterungsmethode einer öffentlichen Instanzmethode entspricht. Es ist wirklich nicht.
Eine Erweiterungsmethode ist nur eine öffentliche statische Hilfsmethode, die eine bequemere Syntax zum Aufruf hat.
Zuerst müssen wir uns fragen, ob es angemessen ist, dass diese Methode eine Instanzmethode der Klasse selbst ist, oder ob sie eher eine statische Methode einer externen Klasse ist. Die Tatsache, dass nur sehr wenige Benutzer der Klasse diese Funktionalität benötigen, weil sie stark lokalisiert ist und nicht wirklich das Verhalten, das die Klasse selbst ausführt, sondern das von einer externen Entität für die Klasse durchgeführte Verhalten, bedeutet, dass sie statisch ist. Der Hauptnachteil ist, dass dieses Verhalten möglicherweise schwieriger zu finden ist, wenn jemand User
hat und seine Repräsentation neu berechnen möchte. Nun, in diesem speziellen Fall ist es ein wenig auf dem Zaun, und Sie könnten in die andere Richtung gehen, aber ich bin in Richtung statische Methode geneigt.
Nachdem wir nun entschieden haben, dass es statisch sein sollte, ist es eine völlig getrennte Frage, ob es eine Erweiterungsmethode sein sollte oder nicht. Dies ist viel subjektiver und geht in den persönlichen Präferenzbereich. Sind die Methoden wahrscheinlich verkettet? Wenn dies der Fall ist, sind Erweiterungsmethoden viel besser als verschachtelte Aufrufe an statische Methoden. Wird es wahrscheinlich in den Dateien, die es verwenden, viel benutzt? Wenn ja, werden Erweiterungsmethoden wahrscheinlich den Code ein wenig vereinfachen, wenn nicht, hilft es nicht so viel oder tut sogar weh. Zum Spielzeug-Beispiel würde ich wahrscheinlich sagen, dass ich persönlich nicht würde, aber ich würde überhaupt kein Problem mit jemandem haben, der es getan hat (schließlich kann man immer noch eine Erweiterungsmethode verwenden, als wäre es eine reguläre öffentliche statische Methode ). Bei einem Nicht-Spielzeug-Beispiel handelt es sich meistens um eine Einzelfallentscheidung. Ein wichtiger Punkt ist, vorsichtig zu sein, welche Klassen Sie erweitern, und sich zu fragen, ob ein Benutzer bereit ist, das Intellisense eines Typs zu verwirren, nur um eine Methode etwas bequemer aufzurufen (dies geht wiederum darauf zurück, wie viel es pro Datei verwendet wird) in).
Erwähnenswert ist auch, dass es einige Randfälle gibt, in denen Erweiterungsmethoden mehr als instanzierte Methoden sein können. Insbesondere durch Verwendung von Inferenzmethoden. Mit einer regulären Instanzmethode ist es einfach, einen Typ oder einen beliebigen Untertyp dieses Typs zu akzeptieren, aber manchmal ist es sinnvoll, den übergebenen Typ anstelle des übergeordneten Typs zurückzugeben. Dies wird insbesondere in flüssigen APIs verwendet. Dies ist jedoch kein sehr allgemeines Beispiel und hängt nur lose mit Ihrer Frage zusammen, daher werde ich nicht weiter darauf eingehen.
Erweiterungsmethoden können sehr nützlich sein, wenn die Klasse eine Schnittstelle implementiert und Sie vermeiden möchten, dieselbe Methode für andere "zukünftige" Klassen zu implementieren, die die gleiche Schnittstelle implementieren. Zum Beispiel implementiert StackExchange IStackExchange und ProgrammersExchange implementiert auch IStackExchange. Ihre Beispiel-Erweiterungsmethode wäre nützlich, um CalcRep nur einmal zu implementieren und nicht für beide Klassen neu zu implementieren. Dies ist genau der Grund für alle Erweiterungsmethoden, die in der statischen Enumerable-Klasse vorhanden sind.
Darüber hinaus sehe ich keinen zwingenden Grund für die Verwendung von Erweiterungsmethoden für eine Klasse, die Sie bereits ändern können. Wenn überhaupt, hat es den Nachteil, dass es bei der Überladungsauflösung zu spät kommt.
Tags und Links .net c# design-patterns extension-methods