Ich habe Anders 'Vortrag über C # 4.0 und eine Vorschau auf C # 5.0 gesehen und darüber nachgedacht, wann optionale Parameter in C # verfügbar sind. Was ist die empfohlene Methode, um Methoden zu deklarieren, für die nicht alle Parameter angegeben werden müssen?
Zum Beispiel hat so etwas wie die FileStream
Klasse ungefähr fünfzehn verschiedene Konstruktoren, die in logische 'Familien' unterteilt werden können, z. B. die folgenden aus einer Zeichenfolge, die aus einer IntPtr
und die aus einer SafeFileHandle
.
FileStream(string,FileMode);
FileStream(string,FileMode,FileAccess);
FileStream(string,FileMode,FileAccess,FileShare);
FileStream(string,FileMode,FileAccess,FileShare,int);
FileStream(string,FileMode,FileAccess,FileShare,int,bool);
Es scheint mir, dass diese Art von Muster vereinfacht werden könnte, indem stattdessen drei Konstruktoren verwendet werden und optionale Parameter für diejenigen verwendet werden, die standardmäßig verwendet werden können, wodurch die verschiedenen Konstruktorfamilien deutlicher werden würden [Anmerkung: Ich weiß, dass diese Änderung nicht möglich ist gemacht in der BCL, ich spreche hypothetisch für diese Art von Situation].
Was denken Sie? Wird es ab C # 4.0 sinnvoller sein, eng verwandte Gruppen von Konstruktoren und Methoden zu einer einzigen Methode mit optionalen Parametern zu machen, oder gibt es einen guten Grund, sich an den traditionellen Mechanismus mit vielen Überladungen zu halten?
quelle
Wenn eine Methodenüberladung normalerweise dasselbe mit einer anderen Anzahl von Argumenten ausführt, werden Standardwerte verwendet.
Wenn eine Methodenüberladung eine Funktion basierend auf ihren Parametern unterschiedlich ausführt, wird die Überladung weiterhin verwendet.
Ich habe in meinen VB6-Tagen optional verwendet und habe es seitdem verpasst. Dadurch wird die Duplizierung von XML-Kommentaren in C # erheblich reduziert.
quelle
Ich habe Delphi schon immer mit optionalen Parametern verwendet. Ich habe stattdessen auf Überlastungen umgestellt.
Wenn Sie mehr Überladungen erstellen, treten ausnahmslos Konflikte mit einem optionalen Parameterformular auf, und Sie müssen diese ohnehin in nicht optionale konvertieren.
Und ich mag die Vorstellung, dass es im Allgemeinen eine Super- Methode gibt, und der Rest sind einfachere Wrapper um diese.
quelle
Foo(A, B, C)
erfordertFoo(A)
,Foo(B)
,Foo(C)
,Foo(A, B)
,Foo(A, C)
,Foo(B, C)
.Ich werde auf jeden Fall die optionale Parameterfunktion von 4.0 verwenden. Es wird das Lächerliche los ...
... und setzt die Werte genau dort, wo der Anrufer sie sehen kann ...
Viel einfacher und viel weniger fehleranfällig. Ich habe dies tatsächlich als Fehler im Überlastungsfall gesehen ...
Ich habe noch nicht mit dem 4.0-Complier gespielt, aber ich wäre nicht schockiert zu erfahren, dass der Complier einfach die Überlastungen für Sie ausgibt.
quelle
Optionale Parameter sind im Wesentlichen Metadaten, die einen Compiler, der einen Methodenaufruf verarbeitet, anweisen, entsprechende Standardeinstellungen an der Aufrufstelle einzufügen. Im Gegensatz dazu bieten Überladungen ein Mittel, mit dem ein Compiler eine von mehreren Methoden auswählen kann, von denen einige möglicherweise selbst Standardwerte liefern. Beachten Sie, dass, wenn man versucht, eine Methode aufzurufen, die optionale Parameter aus Code angibt, der in einer Sprache geschrieben ist, die sie nicht unterstützt, der Compiler die Angabe der "optionalen" Parameter verlangt, aber da das Aufrufen einer Methode ohne Angabe eines optionalen Parameters ist Entspricht dem Aufruf mit einem Parameter, der dem Standardwert entspricht, gibt es kein Hindernis für solche Sprachen, die solche Methoden aufrufen.
Eine wesentliche Folge der Bindung optionaler Parameter am Aufrufstandort ist, dass ihnen Werte zugewiesen werden, die auf der Version des Zielcodes basieren, die dem Compiler zur Verfügung steht. Wenn eine Assembly
Foo
eine MethodeBoo(int)
mit dem Standardwert 5 hat und die AssemblyBar
einen Aufruf von enthältFoo.Boo()
, verarbeitet der Compiler diese alsFoo.Boo(5)
. Wenn der Standardwert auf 6 geändert wird und die AssemblyFoo
neu kompiliert wird,Bar
wird der Aufruf fortgesetzt, esFoo.Boo(5)
sei denn oder bis sie mit dieser neuen Version von neu kompiliert wirdFoo
. Man sollte daher vermeiden, optionale Parameter für Dinge zu verwenden, die sich ändern könnten.quelle
void Foo(int value) … void Foo() { Foo(42); }
. Von außen weiß der Anrufer nicht, welcher Standardwert verwendet wird und wann er sich ändern könnte. dafür müsste man die schriftliche Dokumentation überwachen. Standardwerte für optionale Parameter können als genau das angesehen werden: Dokumentation im Code, wie hoch der Standardwert ist.Es kann argumentiert werden, ob optionale Argumente oder Überladungen verwendet werden sollten oder nicht, aber am wichtigsten ist, dass jedes seinen eigenen Bereich hat, in dem sie unersetzbar sind.
Optionale Argumente sind in Kombination mit benannten Argumenten äußerst nützlich, wenn sie mit einigen Listen mit langen Argumenten und allen Optionen von COM-Aufrufen kombiniert werden.
Überladungen sind äußerst nützlich, wenn die Methode in der Lage ist, viele verschiedene Argumenttypen zu verarbeiten (nur eines von Beispielen) und Castings beispielsweise intern ausführt. Sie füttern es einfach mit einem beliebigen Datentyp, der sinnvoll ist (der von einer vorhandenen Überlastung akzeptiert wird). Kann das mit optionalen Argumenten nicht übertreffen.
quelle
Ich freue mich auf optionale Parameter, da dadurch die Standardeinstellungen näher an der Methode bleiben. Anstelle von Dutzenden von Zeilen für die Überladungen, die nur die "erweiterte" Methode aufrufen, definieren Sie die Methode nur einmal und sehen, was die optionalen Parameter standardmäßig in der Methodensignatur sind. Ich würde lieber schauen:
An Stelle von:
Natürlich ist dieses Beispiel wirklich einfach, aber im Fall von 5 Überlastungen kann es sehr schnell zu einer Überfüllung kommen.
quelle
Einer meiner Lieblingsaspekte bei optionalen Parametern ist, dass Sie sehen, was mit Ihren Parametern passiert, wenn Sie sie nicht angeben, auch ohne zur Methodendefinition zu gehen. Visual Studio zeigt Ihnen einfach den Standardwert für den Parameter an, wenn Sie den Methodennamen eingeben. Bei einer Überladungsmethode müssen Sie entweder die Dokumentation lesen (falls überhaupt verfügbar) oder direkt zur Definition der Methode (falls verfügbar) und zu der Methode navigieren, die die Überladung umschließt.
Insbesondere: Der Dokumentationsaufwand kann mit der Anzahl der Überladungen schnell zunehmen, und Sie werden wahrscheinlich bereits vorhandene Kommentare aus den vorhandenen Überladungen kopieren. Dies ist ziemlich ärgerlich, da es keinen Wert erzeugt und das DRY-Prinzip bricht . Andererseits gibt es mit einem optionalen Parameter genau eine Stelle, an der alle Parameter dokumentiert sind, und Sie sehen ihre Bedeutung sowie ihre Standardwerte während der Eingabe.
Last but not least haben Sie als Konsument einer API möglicherweise nicht einmal die Möglichkeit, die Implementierungsdetails zu überprüfen (wenn Sie nicht über den Quellcode verfügen), und haben daher keine Chance zu sehen, zu welcher Supermethode die überladenen gehören wickeln. Sie müssen also das Dokument nicht lesen und hoffen, dass dort alle Standardwerte aufgelistet sind. Dies ist jedoch nicht immer der Fall.
Natürlich ist dies keine Antwort, die alle Aspekte behandelt, aber ich denke, sie fügt eine hinzu, die bisher nicht behandelt wurde.
quelle
Obwohl dies (angeblich?) Zwei konzeptionell äquivalente Möglichkeiten sind, Ihre API von Grund auf neu zu modellieren, weisen sie leider einen subtilen Unterschied auf, wenn Sie die Abwärtskompatibilität der Laufzeit für Ihre alten Clients in freier Wildbahn berücksichtigen müssen. Mein Kollege (danke Brent!) Hat mich auf diesen wunderbaren Beitrag hingewiesen : Versionierungsprobleme mit optionalen Argumenten . Einige Zitate daraus:
quelle
Eine Einschränkung optionaler Parameter ist die Versionierung, bei der ein Refactor unbeabsichtigte Konsequenzen hat. Ein Beispiel:
Anfangscode
Angenommen, dies ist einer von vielen Aufrufern der oben genannten Methode:
Hier ist das Ereignis nicht still und wird als kritisch behandelt.
Nehmen wir nun an, nach einem Refactor stellen wir fest, dass alle Fehler den Benutzer trotzdem auffordern, sodass wir das stille Flag nicht mehr benötigen. Also entfernen wir es.
Nach dem Refactor
Der frühere Aufruf wird immer noch kompiliert, und sagen wir, er rutscht unverändert durch den Refactor:
Nun
false
wird eine unbeabsichtigte Wirkung hat, wird das Ereignis nicht mehr so kritisch behandelt werden.Dies kann zu einem geringfügigen Fehler führen, da kein Kompilierungs- oder Laufzeitfehler auftritt (im Gegensatz zu einigen anderen Einschränkungen von Optionen wie diesem oder diesem ).
Beachten Sie, dass es viele Formen desselben Problems gibt. Eine andere Form wird hier beschrieben .
Beachten Sie auch, dass die strikte Verwendung benannter Parameter beim Aufrufen der Methode das folgende Problem vermeidet :
HandleError("Disk is full", silent:false)
. Es ist jedoch möglicherweise nicht praktikabel anzunehmen, dass alle anderen Entwickler (oder Benutzer einer öffentlichen API) dies tun.Aus diesen Gründen würde ich die Verwendung optionaler Parameter in einer öffentlichen API (oder sogar einer öffentlichen Methode, wenn diese weit verbreitet sein könnte) vermeiden, es sei denn, es gibt andere zwingende Überlegungen.
quelle
Beide optionalen Parameter, Methodenüberladung, haben ihren eigenen Vor- oder Nachteil. Es hängt von Ihrer Präferenz ab, zwischen ihnen zu wählen.
Optionaler Parameter: Nur in .Net 4.0 verfügbar. Optionaler Parameter Reduzieren Sie Ihre Codegröße. Sie können keine Parameter definieren und ref
überladene Methoden: Sie können Out- und Ref-Parameter definieren. Die Codegröße nimmt zu, aber überladene Methoden sind leicht zu verstehen.
quelle
In vielen Fällen werden optionale Parameter verwendet, um die Ausführung zu wechseln. Beispielsweise:
Der Discount-Parameter wird hier verwendet, um die if-then-else-Anweisung einzugeben. Es gibt den Polymorphismus, der nicht erkannt wurde, und der dann als Wenn-Dann-Sonst-Anweisung implementiert wurde. In solchen Fällen ist es viel besser, die beiden Kontrollflüsse in zwei unabhängige Methoden aufzuteilen:
Auf diese Weise haben wir die Klasse sogar davor geschützt, einen Anruf ohne Rabatt zu erhalten. Dieser Anruf würde bedeuten, dass der Anrufer denkt, dass es den Rabatt gibt, aber tatsächlich gibt es überhaupt keinen Rabatt. Ein solches Missverständnis kann leicht einen Fehler verursachen.
In solchen Fällen möchte ich keine optionalen Parameter haben, sondern den Aufrufer zwingen, das Ausführungsszenario explizit auszuwählen, das seiner aktuellen Situation entspricht.
Die Situation ist sehr ähnlich zu Parametern, die null sein können. Das ist eine ebenso schlechte Idee, wenn die Implementierung auf Aussagen wie z
if (x == null)
.Eine detaillierte Analyse finden Sie unter folgenden Links: Vermeiden optionaler Parameter und Vermeiden von Nullparametern
quelle
So fügen Sie ein Kinderspiel hinzu, wenn eine Überladung anstelle von Optionen verwendet werden soll:
Wenn Sie eine Reihe von Parametern haben, die nur zusammen Sinn machen, führen Sie keine Optionen ein.
Oder allgemeiner: Wenn Ihre Methodensignaturen Verwendungsmuster aktivieren, die keinen Sinn ergeben, beschränken Sie die Anzahl der Permutationen möglicher Aufrufe. Zum Beispiel durch die Verwendung von Überladungen anstelle von Optionen (diese Regel gilt übrigens auch, wenn Sie mehrere Parameter desselben Datentyps haben; hier können Geräte wie Factory-Methoden oder benutzerdefinierte Datentypen hilfreich sein).
Beispiel:
quelle