Sollten Sie erklären Methoden unter Verwendung von überlast oder optionale Parameter, die in C# 4.0?

StackOverflow https://stackoverflow.com/questions/251868

Frage

Ich war gerade Anders' zu reden, C# 4.0 und Vorschau auf C# 5.0, und es hat mir denken über, wenn der optionale Parameter sind verfügbar in C#, was ist der empfohlene Weg, um zu erklären, Methoden, die nicht alle Parameter angegeben?

Zum Beispiel so etwas wie die FileStream Klasse hat über fünfzehn verschiedene Konstruktoren, die können unterteilt werden in logische 'Familien', die z.B.die unten aus einer Zeichenfolge, die aus einer IntPtr und diejenigen, die von einem 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 kann vereinfacht werden, indem Sie mit drei Konstruktoren statt, und mit optionalen Parametern für diejenigen, die kann vorgegeben werden, welches die verschiedenen Familien von Konstruktoren mehr distinct [Hinweis:Ich weiß, diese änderung wird nicht werden in der BCL, ich spreche rein hypothetisch für diese Art von situation].

Was denkst du?Von C# 4.0 wird es mehr Sinn machen, um eng Verwandte Gruppen von Konstruktoren und Methoden einer einzelnen Methode mit optionalen Parametern, oder gibt es einen guten Grund für die stick mit der traditionellen viele-überlast-Mechanismus?

War es hilfreich?

Lösung

Ich würde Folgendes beachten:

  • Haben Sie Ihren Code müssen von Sprachen verwendet werden, die keine optionalen Parameter unterstützen? Wenn ja, sollten Sie auch die Überlastung.
  • Haben Sie Mitglieder in Ihrem Team, die optionalen Parameter heftig widersetzen? (Manchmal ist es einfacher, mit einer Entscheidung leben Sie nicht mögen, als den Fall zu argumentieren.)
  • Sind Sie sicher, dass Ihre Standardeinstellungen nicht zwischen Ihrer Code erstellt ändern oder wenn sie könnten, werden Ihre Anrufer mit, dass in Ordnung sein?

Ich habe nicht geprüft, wie die Standardeinstellungen arbeiten werden, aber ich würde davon ausgehen, dass die Standardwerte in den Aufrufcode gebacken werden, ganz ähnlich wie Referenzen Felder const. Das ist in der Regel in Ordnung - Änderungen auf einen Standardwert sind ziemlich bedeutende sowieso -. Aber das sind die Dinge, die

Andere Tipps

Wenn ein -Methodenüberladung normalerweise führt die gleiche Sache mit einer unterschiedlichen Anzahl von Argumenten, dann werden Standardwerte verwendet werden.

Wenn ein -Methodenüberladung eine Funktion ausführt, anders basierend auf seinen Parametern dann wird auch weiterhin eine Überlastung verwendet werden.

Ich habe optional wieder in meinem VB6 Tage und haben, da es verpasst, wird es eine Menge von XML-Kommentar Duplizierung in C # reduzieren.

Ich habe mit Delphi, mit optionalen Parametern, für immer. Ich habe stattdessen mit Überlastungen geschaltet.

Weil, wenn Sie gehen, um mehr Überlastungen zu erstellen, werden Sie immer mit einer optionalen Parameter Form confict; und Sie werden dann konvertieren müssen sie nicht optional sowieso.

Und ich den Begriff mag, dass es im Allgemeinen ein Super Methode, und der Rest ist einfacher Wrapper um, dass ein.

Ich werde auf jeden Fall die optionalen Parameter Funktion von 4,0 werden. Es entledigt sich der lächerlichen ...

public void M1( string foo, string bar )
{
   // do that thang
}

public void M1( string foo )
{
  M1( foo, "bar default" ); // I have always hated this line of code specifically
}

... und legt die Werte genau dort, wo der Anrufer sie sehen kann ...

public void M1( string foo, string bar = "bar default" )
{
   // do that thang
}

Viel einfacher und viel weniger fehleranfällig. Ich habe dies tatsächlich als Fehler im Überlastfall gesehen ...

public void M1( string foo )
{
   M2( foo, "bar default" );  // oops!  I meant M1!
}

Ich habe nicht mit dem 4.0 complier noch gespielt, aber ich würde nicht schockiert zu erfahren, dass die complier einfach die Überlastungen für Sie aussendet.

Optionale Parameter sind im Wesentlichen ein Teil der Metadaten, die einen Kompilierer anweist, dass ein Methodenaufruf bearbeitet entsprechende Standardwerte an der Aufrufstelle einzufügen. Im Gegensatz dazu bieten Überlastungen ein Mittel, mit dem ein Compiler eine aus einer Reihe von Methoden auswählen können, selbst von denen einige liefern könnten Standardwerte. Beachten Sie, dass, wenn man versucht, eine Methode aufrufen, die in einer Sprache geschrieben, optionale Parameter von Code gibt, die sie nicht unterstützt, wird der Compiler erfordern, dass die „optional“ Parameter angegeben werden, aber da ohne Aufruf einer Methode ein optionaler Parameter spezifiziert ist äquivalent mit einem Parameter gleich dem Standardwert zu nennen, gibt es kein Hindernis für diese Sprachen solche Methoden aufrufen.

Eine wesentliche Folge an der Aufrufstelle der optionalen Parameter verbindlich ist, dass sie Werte von der Version des Zielcodes auf Basis zugeordnet werden, die an den Compiler zur Verfügung steht. Wenn eine Baugruppe Foo Boo(int) ein Verfahren mit einem Standardwert von 5 hat, und Montag Bar enthält einen Aufruf an Foo.Boo(), wird der Compiler, der als Foo.Boo(5) verarbeiten. Wenn der Standardwert auf 6 und Montag Foo wird neu kompiliert geändert wird, wird Bar weiterhin Foo.Boo(5) nennen, es sei denn, oder bis es mit dieser neuen Version von Foo neu kompiliert wird. Man sollte also mit dem optionalen Parameter für die Dinge vermeiden, die sich ändern können.

Ich freue mich auf optionale Parameter, weil es hält, was die Vorgaben der Methode näher sind. Anstatt also Dutzende von Leitungen für die Überlastung, die die „erweiterte“ Methode nur aufrufen, müssen Sie nur die Methode einmal definieren und Sie können, was die optionale Parameter standardmäßig in der Methodensignatur sehen. Ich würde lieber sehen:

public Rectangle (Point start = Point.Zero, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

Statt dessen:

public Rectangle (Point start, int width, int height)
{
    Start = start;
    Width = width;
    Height = height;
}

public Rectangle (int width, int height) :
    this (Point.Zero, width, height)
{
}

Natürlich ist dieses Beispiel wirklich einfach, aber der Fall in dem OP mit 5 Überlastungen können die Dinge voll ganz schnell bekommen.

Es kann argumentiert werden, ob optionale Argumente oder Überlastungen verwendet werden sollen oder nicht, aber am wichtigsten ist, hat jeweils ihren eigenen Bereich, in dem sie unersetzlich sind.

Optionale Argumente, wenn sie in Kombination mit benannten Argumenten verwendet wird, ist äußerst nützlich, wenn sie mit einigen langen Argumentlisten-with-all-optionals kombiniert von COM-Aufrufen.

Überlastungen sind extrem nützlich, wenn Methode auf vielen verschiedenen Argumenttypen arbeiten kann (nur eines der Beispiele), und tut Gussteile intern, zum Beispiel; Sie füttern sie nur mit einem beliebigen Datentyp, die Sinn macht (das durch eine bestehende Überlastung angenommen wird). Kann nicht mit optionalen Argumenten schlagen.

Einer meiner Favoriten Aspekte der optionale Parameter ist, dass Sie sehen, was Ihre Parameter passiert, wenn Sie sie nicht bieten, auch ohne auf die Methodendefinition zu gehen. Visual Studio zeigt Ihnen einfach den Standardwert für den Parameter, wenn Sie die Methodennamen eingeben. Bei einer Überlastung Methode werden Sie entweder mit dem Lesen die Dokumentation (auch wenn vorhanden) oder mit direkt der Navigation zu der Methode der Definition (falls vorhanden) und die Methode für die Überlastung Wraps fest.

Im Einzelnen: der Dokumentationsaufwand mit der Menge an Überlastungen schnell erhöhen kann, und Sie werden wahrscheinlich das Kopieren bereits bestehende Kommentare aus den bestehenden Überlastungen enden. Das ist ziemlich ärgerlich, da es keinen Wert erzeugen und bricht die DRY-Prinzip ). Auf der anderen Seite, mit einem optionalen Parameter gibt es genau einen Ort in dem alle Parameter dokumentiert und Sie sehen ihre Bedeutung sowie ihre Standardwerte , während Sie tippen.

Last but not least, wenn Sie bei dem Verbraucher der API sind, können Sie nicht einmal die Möglichkeit, die Details der Implementierung Inspektion (wenn Sie nicht über den Quellcode haben) und haben daher keine Chance, auf die Super-Methode zu sehen die überlasteten diejenigen wickeln. So sind Sie stecken mit dem Doc zu lesen und zu hoffen, dass alle Standardwerte dort aufgeführt sind, aber dies ist nicht immer der Fall.

Natürlich ist dies nicht eine Antwort, die alle Aspekte behandelt, aber ich denke, es ist ein fügt hinzu, die wird bisher nicht abgedeckt.

Ein Nachteil der optionale Parameter ist die Versionsverwaltung, wo umgestalten hat unbeabsichtigte Folgen.Ein Beispiel:

Initial code

public string HandleError(string message, bool silent=true, bool isCritical=true)
{
  ...
}

Nehme an, dies ist einer von vielen Anrufern von der oben beschriebenen Methode:

HandleError("Disk is full", false);

Hier wird das Ereignis nicht still und wird behandelt, als kritisch.

Lassen Sie uns jetzt sagen, nachdem umgestalten finden wir, dass alle Fehler, die den Benutzer auffordern, die sowieso, also wir müssen nicht mehr die silent-flag.So entfernen wir es.

Nach umgestalten

Der ehemalige rufen Sie noch kompiliert, und lassen Sie uns sagen, es rutscht durch die Umgestaltung unverändert:

public string HandleError(string message, /*bool silent=true,*/ bool isCritical=true)
{
  ...
}

...

// Some other distant code file:
HandleError("Disk is full", false);

Jetzt false haben Sie eine unbeabsichtigte Wirkung, die Veranstaltung wird nicht mehr behandelt werden als kritisch.

Könnte dies in einem subtilen Mangel, da es keinen Compiler oder Laufzeit-Fehler (im Gegensatz zu einigen anderen Einschränkungen von Optionen, wie diese oder diese).

Beachten Sie, dass es sind viele Formen dieses problem.Eine andere form ist beschrieben hier.

Beachten Sie auch, dass streng die Verwendung von benannten Parametern beim Aufruf der Methode vermeiden Sie das Problem, wie diese: HandleError("Disk is full", silent:false).Jedoch, kann es nicht zweckmäßig sein, anzunehmen, dass alle anderen Entwickler (oder der Benutzer eines öffentlichen API), die es tun werden.

Aus diesen Gründen würde ich vermeiden Sie die Verwendung von optionalen Parametern, die in eine öffentliche API (oder sogar eine öffentliche Methode wenn es kann allgemein verwendet werden), es sei denn, es gibt andere zwingende Erwägungen.

Sowohl optionaler Parameter, -Methodenüberladung haben einen eigenen Vorteil oder disadvantage.it nach Präferenz hängt zwischen ihnen zu wählen.

Optional Parameter: nur in .NET 4.0 verfügbar. optionale Parameter Ihre Codegröße reduzieren. Sie können nicht definieren und ref Parameter

ladenen Methoden: Sie können Out und ref Parameter definieren. Codegröße erhöht sich aber überladene Methode ist leicht zu verstehen sind.

In vielen Fällen optionale Parameter werden verwendet, die Ausführung zu wechseln. Zum Beispiel:

decimal GetPrice(string productName, decimal discountPercentage = 0)
{

    decimal basePrice = CalculateBasePrice(productName);

    if (discountPercentage > 0)
        return basePrice * (1 - discountPercentage / 100);
    else
        return basePrice;
}

Discount Parameter hier verwendet, um die if-then-else-Anweisung zu füttern. Es ist der Polymorphismus, der nicht erkannt wurde, und dann wurde es als if-then-else-Anweisung umgesetzt. In solchen Fällen ist es viel besser, die beiden Steuerströme in zwei unabhängige Methoden aufzuteilen:

decimal GetPrice(string productName)
{
    decimal basePrice = CalculateBasePrice(productName);
    return basePrice;
}

decimal GetPrice(string productName, decimal discountPercentage)
{

    if (discountPercentage <= 0)
        throw new ArgumentException();

    decimal basePrice = GetPrice(productName);

    decimal discountedPrice = basePrice * (1 - discountPercentage / 100);

    return discountedPrice;

}

Auf diese Weise haben wir auch die Klasse von Empfang eines Anrufs mit Null Rabatt geschützt. Der Anruf würde bedeuten, dass der Anrufer denkt, dass der Rabatt ist, aber in Wirklichkeit gibt es gar keinen Rabatt. Solche Missverständnisse können Sie einen Fehler leicht verursachen.

In Fällen wie diesem, ziehe ich es nicht optionale Parameter haben, aber der Anrufer explizit zu zwingen, die Ausführung Szenario auswählen, die seine aktuelle Situation entspricht.

Die Situation ist sehr ähnliche Parameter zu haben, die null sein kann. Das ist ebenso schlechte Idee, wenn die Umsetzung zu Aussagen wie if (x == null) siedet.

Sie können detaillierte Analyse auf diesen Links finden: Vermeiden Optionale Parameter und Vermeidung von Null Parameter

Während sie (angeblich?) Zwei konzeptionell äquivalente Möglichkeiten zur Verfügung, damit Sie Ihre API von Grunde auf neu zu modellieren, sie leider etwas subtilen Unterschied, wenn Sie Laufzeit der Abwärtskompatibilität für Ihren alten Kunden in der freien Natur berücksichtigen müssen. Mein Kollege (Dank Brent!) Wies mich auf diese wunderbar Beitrag: Versionierung Probleme mit optionalen Argumenten . Einige Zitat von ihm:

  

Der Grund, dass optionale Parameter C # 4 in die eingeführt wurden,   Zunächst war COM-Interop zu unterstützen. Das ist es. Und jetzt, wir sind   Lernen über die vollen Auswirkungen dieser Tatsache. Wenn Sie eine   Verfahren mit optionalen Parametern, man kann nie eine Überlastung hinzufügen mit   zusätzliche optionale Parameter aus Angst einen Compiler-verursachen   brechende Änderung. Und man kann nie eine bestehende Überlastung entfernen, wie   dies war schon immer ein Laufzeitbruch verändert. Sie ziemlich viel brauchen   zu behandeln sie wie eine Schnittstelle. Ihre einzige Möglichkeit ist in diesem Fall zu   eine neue Methode mit einem neuen Namen schreiben. So beachten Sie dies, wenn Sie planen,   verwenden optionale Argumente in Ihrer APIs.

So fügen Sie ein Kinderspiel, wenn eine Überlastung statt optionals zu verwenden:

Wenn Sie eine Reihe von Parametern, die nur Sinn machen zusammen, nicht vorstellen optionals auf sie.

oder allgemeiner, wenn Ihre Methode Signaturen Nutzungsmustern ermöglichen, die die Anzahl der Permutationen der möglichen Anrufe keinen Sinn machen, beschränken. Zum Beispiel durch Überlastungen statt optionals verwenden. (Diese Regel auch gilt, wenn Sie mehrere Parameter des gleichen Datentypen haben, nebenbei gesagt, hier Geräte wie Fabrikmethoden oder benutzerdefinierte Datentypen können helfen)

Beispiel:

enum Match {
    Regex,
    Wildcard,
    ContainsString,
}

// Don't: This way, Enumerate() can be called in a way
//         which does not make sense:
IEnumerable<string> Enumerate(string searchPattern = null,
                              Match match = Match.Regex,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);

// Better: Provide only overloads which cannot be mis-used:
IEnumerable<string> Enumerate(SearchOption searchOption = SearchOption.TopDirectoryOnly);
IEnumerable<string> Enumerate(string searchPattern, Match match,
                              SearchOption searchOption = SearchOption.TopDirectoryOnly);
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top