Betrachten Sie die folgende Methode:
public List<Guid> ReturnEmployeeIds(bool includeManagement = false)
{
}
Und der folgende Aufruf:
var ids = ReturnEmployeeIds(true);
Für einen Entwickler, der neu im System ist, wäre es ziemlich schwierig zu erraten, was true
passiert ist. Bewegen Sie den Mauszeiger zuerst über den Methodennamen oder gehen Sie zur Definition (keine der beiden Aufgaben ist im Geringsten eine große Aufgabe). Ist es jedoch aus Gründen der Lesbarkeit sinnvoll, Folgendes zu schreiben:
var ids = ReturnEmployeeIds(includeManagement: true);
Gibt es irgendwo, wo formal diskutiert wird, ob optionale Parameter explizit angegeben werden sollen oder nicht, wenn der Compiler dies nicht benötigt?
Der folgende Artikel beschreibt bestimmte Codierungskonventionen: https://msdn.microsoft.com/en-gb/library/ff926074.aspx
Etwas ähnliches wie der obige Artikel wäre großartig.
c#
coding-style
readability
parameters
JᴀʏMᴇᴇ
quelle
quelle
boolean
Methodenargumente verwenden und wie?Antworten:
Ich würde sagen, dass in der C # -Welt eine Aufzählung eine der besten Optionen ist.
Auf diese Weise müssten Sie genau angeben, was Sie tun, und jeder Benutzer würde sehen, was passiert. Es ist auch sicher für zukünftige Erweiterungen;
Also meiner Meinung nach hier:
enum> optional enum> optional bool
Bearbeiten: Aufgrund der Diskussion mit LeopardSkinPillBoxHat unten in Bezug auf eine
[Flags]
Aufzählung, die in diesem speziellen Fall eine gute Anpassung sein könnte (da wir speziell über das Einschließen / Ausschließen von Dingen sprechen), schlage ich stattdessen vor,ISet<WhatToInclude>
als Parameter zu verwenden.Es handelt sich um ein "modernes" Konzept mit mehreren Vorteilen, das sich hauptsächlich aus der Tatsache ergibt, dass es in die LINQ-Kollektionsfamilie passt, aber auch
[Flags]
maximal 32 Gruppen umfasst. Der Hauptnachteil der VerwendungISet<WhatToInclude>
ist, wie schlecht Sets in der C # -Syntax unterstützt werden:Einige davon könnten durch Hilfsfunktionen gemildert werden, sowohl zum Erzeugen der Menge als auch zum Erzeugen von "Verknüpfungsmengen", wie z
quelle
enum
Ansatz, aber ich bin kein großer Fan des MischensInclude
undExclude
in den gleichen ,enum
wie es ist schwieriger zu begreifen , was los ist . Wenn dies wirklich erweiterbar sein soll, können Sie es zu einem[Flags]
enum
machen, sie alle machenIncludeXxx
und ein bereitstellen,IncludeAll
das auf festgelegt istInt32.MaxValue
. Dann können Sie 2ReturnEmployeeIds
Überladungen bereitstellen - eine, die alle Mitarbeiter zurückgibt , und eine, die einenWhatToInclude
Filterparameter verwendet, der eine Kombination von Enum-Flags sein kann.[Flags]
ist ein Relikt und sollte nur aus Legacy- oder Performance-Gründen verwendet werden. Ich denke, aISet<WhatToInclude>
ist ein weitaus besseres Konzept (leider fehlt C # etwas syntaktischer Zucker, um es schön zu benutzen).Flags
Ansatz, weil er es dem Anrufer erlaubt,WhatToInclude.Managers | WhatToInclude.Cleaners
bitweise zu passieren oder genau auszudrücken , wie der Anrufer ihn verarbeiten wird (dh Manager oder Reinigungskräfte). Intuitiver syntaktischer Zucker :-)Es ist fraglich, ob dies "guter Stil" ist, aber IMHO ist dies zumindest kein schlechter Stil und nützlich, solange die Codezeile nicht "zu lang" wird. Ohne benannte Parameter ist es auch ein üblicher Stil, eine erklärende Variable einzuführen:
Dieser Stil ist wahrscheinlich bei C # -Programmierern häufiger anzutreffen als bei der benannten Parametervariante, da benannte Parameter vor Version 4.0 nicht Bestandteil der Sprache waren. Die Auswirkung auf die Lesbarkeit ist nahezu gleich, es wird lediglich eine zusätzliche Codezeile benötigt.
Also meine Empfehlung: Wenn Sie glauben, dass die Verwendung einer Aufzählung oder von zwei Funktionen mit unterschiedlichen Namen "übertrieben" ist, oder wenn Sie die Signatur aus einem anderen Grund nicht ändern möchten oder können, fahren Sie fort und verwenden Sie den benannten Parameter.
quelle
includeManagement
als lokal markierenconst
und die Verwendung von zusätzlichem Speicher vermeiden.Wenn Sie auf Code stoßen wie:
Sie können so ziemlich garantieren, dass das, was sich im Inneren befindet,
{}
ungefähr so aussieht:Mit anderen Worten, der Boolesche Wert wird verwendet, um zwischen zwei Vorgehensweisen zu wählen: Die Methode hat zwei Verantwortlichkeiten. Damit ist so ziemlich ein Codegeruch garantiert . Wir sollten uns immer darum bemühen, dass Methoden nur eines tun; Sie haben nur eine Verantwortung. Ich würde daher argumentieren, dass beide Ihrer Lösungen der falsche Ansatz sind. Die Verwendung optionaler Parameter ist ein Zeichen dafür, dass die Methode mehr als eine Sache tut. Boolesche Parameter sind ebenfalls ein Zeichen dafür. Wenn Sie beide haben, sollten Sie unbedingt die Alarmglocken läuten lassen. Sie sollten daher die beiden verschiedenen Funktionssätze in zwei separate Methoden umgestalten. Geben Sie den Methoden Namen, die verdeutlichen, was sie tun, zB:
Dies verbessert sowohl die Lesbarkeit des Codes als auch die Einhaltung der SOLID-Prinzipien.
BEARBEITEN Wie bereits in den Kommentaren erwähnt, wird der Fall, dass diese beiden Methoden wahrscheinlich über eine gemeinsame Funktionalität verfügen, und dass die gemeinsame Funktionalität wahrscheinlich am besten in eine private Funktion eingebunden wird, die einen booleschen Parameter benötigt, um einen bestimmten Aspekt ihrer Funktion zu steuern .
Sollte in diesem Fall der Parametername angegeben werden, obwohl der Compiler ihn nicht benötigt? Ich würde vorschlagen, dass es keine einfache Antwort darauf gibt und dass von Fall zu Fall ein Urteil erforderlich ist:
Ist die Quelldatei und die Methoden klein und leicht verständlich? Wenn ja, dann ist der genannte Parameter wahrscheinlich ein Rauschen. Wenn nein, kann es sehr hilfreich sein. Wenden Sie die Regel also den Umständen entsprechend an.
quelle
public List<Guid> ReturnEmployeeIds(bool includeManagement) { calculateSomethingHereForBothBranches; if (includeManagement) return something else return something else }
, dass eine Aufteilung in zwei Methoden wahrscheinlich bedeutet, dass diese beiden Methoden das Ergebnis der ersten Berechnung als Parameter benötigen.Ja stimmt. Selbstdokumentierender Code ist eine schöne Sache. Wie andere Antworten gezeigt haben, gibt es Möglichkeiten, die Mehrdeutigkeit des Aufrufs zu beseitigen,
ReturnEmployeeIds
indem Sie eine Aufzählung, andere Methodennamen usw. verwenden. Manchmal können Sie den Fall jedoch nicht wirklich vermeiden, möchten ihn jedoch nicht verwenden sie überall (es sei denn, Sie mögen die Ausführlichkeit von Visual Basic).Zum Beispiel kann es helfen, einen Anruf zu klären, aber nicht unbedingt einen anderen.
Ein benanntes Argument kann hier hilfreich sein:
Fügen Sie nicht viel zusätzliche Klarheit hinzu (ich gehe davon aus, dass dies eine Aufzählung analysiert):
Dies kann die Übersichtlichkeit beeinträchtigen (wenn eine Person nicht versteht, welche Kapazität in einer Liste enthalten ist):
Oder wo es egal ist, weil Sie gute Variablennamen verwenden:
Nicht AFAIK. Lassen Sie uns jedoch beide Teile ansprechen: Das einzige Mal, dass Sie benannte Parameter explizit verwenden müssen, besteht darin, dass der Compiler dies verlangt (im Allgemeinen, um die Mehrdeutigkeit von Anweisungen zu beseitigen). Ansonsten können Sie sie jederzeit verwenden. In diesem Artikel von MS finden Sie einige Vorschläge, wann Sie sie verwenden möchten, die jedoch nicht besonders eindeutig sind: https://msdn.microsoft.com/en-us/library/dd264739.aspx .
Persönlich verwende ich sie am häufigsten, wenn ich benutzerdefinierte Attribute erstelle oder eine neue Methode ausarbeite, bei der sich meine Parameter ändern oder neu angeordnet werden können (b / c-benannte Attribute können in beliebiger Reihenfolge aufgelistet werden). Außerdem verwende ich sie nur, wenn ich einen statischen Wert inline bereitstelle. Wenn ich eine Variable übergebe, versuche ich, gute Variablennamen zu verwenden.
Letztendlich kommt es auf die persönlichen Vorlieben an. Natürlich gibt es einige Anwendungsfälle, in denen Sie benannte Parameter verwenden müssen, aber danach müssen Sie einen Beurteilungsaufruf durchführen. IMO - Verwenden Sie es überall dort, wo Sie glauben, dass es Ihnen dabei hilft, Ihren Code zu dokumentieren, Mehrdeutigkeiten zu verringern oder Methodensignaturen zu schützen.
quelle
Wenn der Parameter aus dem (vollständig qualifizierten) Namen der Methode hervorgeht und seine Existenz dem entspricht, was die Methode tun soll, sollten Sie dies nicht tun die benannte Parametersyntax verwenden. Zum Beispiel ist
ReturnEmployeeName(57)
es ziemlich offensichtlich, dass dies57
die ID des Mitarbeiters ist, so dass es überflüssig ist, sie mit der benannten Parametersyntax zu kommentieren.Mit
ReturnEmployeeIds(true)
, wie Sie sagten, ist es unmöglich, herauszufinden, wastrue
bedeutet , wenn Sie sich die Deklaration / Dokumentation der Funktion nicht ansehen - Sie also sollten also die benannte Parametersyntax verwenden.Betrachten Sie nun diesen dritten Fall:
Die benannte Parametersyntax wird hier nicht verwendet, aber da wir eine Variable übergeben
ReturnEmployeeIds
und diese Variable einen Namen hat, bedeutet dieser Name zufällig dasselbe wie der Parameter, an den wir ihn übergeben (dies ist häufig der Fall - aber nicht immer!) - wir brauchen die benannte Parametersyntax nicht, um die Bedeutung des Codes zu verstehen.Die Regel ist also einfach: Wenn Sie anhand des Methodenaufrufs leicht verstehen können , was der Parameter bedeuten soll, verwenden Sie den benannten Parameter nicht. Wenn Sie dies nicht können, ist dies eine Lücke in der Lesbarkeit des Codes, und Sie möchten diese wahrscheinlich beheben (natürlich nicht um jeden Preis, aber Sie möchten in der Regel, dass der Code lesbar ist). Der Fix muss nicht als Parameter bezeichnet werden. Sie können den Wert auch zuerst in eine Variable einfügen oder die in den anderen Antworten vorgeschlagenen Methoden verwenden, sofern das Endergebnis das Verständnis der Funktionsweise des Parameters erleichtert.
quelle
ReturnEmployeeName(57)
sofort klar wird, dass57
es sich um die ID handelt.GetEmployeeById(57)
auf der anderen Seite ...ReturnEmployeeName
, um Fälle zu veranschaulichen, in denen auch ohne ausdrückliche Erwähnung des Parameters im Namen der Methode zu erwarten ist, dass die Leute herausfinden, was es ist. Auf jeden Fall ist es bemerkenswert, dassReturnEmployeeName
Sie im Gegensatz zu vernünftigerweise nichtReturnEmployeeIds
in etwas umbenennen können , das die Bedeutung hinter den Parametern anzeigt.Ja, ich denke es ist ein guter Stil.
Dies ist am sinnvollsten für Boolesche und ganzzahlige Konstanten.
Wenn Sie eine Variable übergeben, sollte der Variablenname die Bedeutung verdeutlichen. Wenn dies nicht der Fall ist, sollten Sie der Variablen einen besseren Namen geben.
Wenn Sie eine Zeichenfolge übergeben, ist die Bedeutung oft klar. Zum Beispiel, wenn ich einen Aufruf von GetFooData ("Oregon") sehe, kann ein Leser davon ausgehen, dass der Parameter ein Zustandsname ist.
Natürlich sind nicht alle Zeichenfolgen offensichtlich. Wenn ich GetFooData ("M") sehe, habe ich keine Ahnung, was "M" bedeutet, es sei denn, ich kenne die Funktion. Wenn es sich um eine Art Code handelt, wie z. B. M = "Angestellte auf Managementebene", sollten wir wahrscheinlich eher eine Aufzählung als ein Literal haben und der Aufzählungsname sollte klar sein.
Und ich nehme an, eine Saite könnte irreführend sein. Vielleicht bezieht sich "Oregon" in meinem obigen Beispiel nicht auf den Staat, sondern auf ein Produkt oder einen Kunden oder was auch immer.
Aber GetFooData (true) oder GetFooData (42) ... das könnte alles bedeuten. Benannte Parameter sind eine wundervolle Möglichkeit, sich selbst zu dokumentieren. Ich habe sie mehrmals genau zu diesem Zweck verwendet.
quelle
Es gibt keine eindeutigen Gründe, warum diese Art von Syntax überhaupt verwendet werden sollte.
Versuchen Sie im Allgemeinen, boolesche Argumente zu vermeiden, die in einiger Entfernung willkürlich aussehen können.
includeManagement
wird (höchstwahrscheinlich) das Ergebnis stark beeinflussen. Aber das Argument scheint "wenig Gewicht" zu haben.Die Verwendung einer Aufzählung wurde diskutiert. Sie sieht nicht nur so aus, als würde das Argument "mehr Gewicht haben", sondern bietet auch Skalierbarkeit für die Methode. Dies ist jedoch möglicherweise nicht in allen Fällen die beste Lösung, da Ihre Methode
ReturnEmployeeIds
neben demWhatToInclude
-enum skaliert werden muss (siehe NiklasJ Antwort von ). Dies kann später zu Kopfschmerzen führen.WhatToInclude
Beachten Sie Folgendes : Wenn Sie das -enum skalieren, aber nicht dieReturnEmployeeIds
-methode. Es könnte dann einenArgumentOutOfRangeException
(besten Fall) werfen oder etwas völlig Unerwünschtes (null
oder ein leeresList<Guid>
) zurückgeben. Was in einigen Fällen den Programmierer verwirren könnte, besonders wenn SieReturnEmployeeIds
sich in einer Klassenbibliothek befinden, in der der Quellcode nicht sofort verfügbar ist.Man wird davon ausgehen, dass
WhatToInclude.Trainees
das funktionieren wird, wennWhatToInclude.All
sieht, dass die Auszubildenden eine "Teilmenge" von allen sind.Dies hängt (natürlich) davon ab, wie
ReturnEmployeeIds
es implementiert ist.In Fällen, in denen ein boolesches Argument übergeben werden könnte, versuche ich, es in Ihrem Fall stattdessen in zwei Methoden (oder mehr, falls erforderlich) aufzuteilen. man könnte extrahieren
ReturnAllEmployeeIds
,ReturnManagementIds
undReturnRegularEmployeeIds
. Dies deckt alle Grundlagen ab und ist für jeden, der es umsetzt, absolut selbsterklärend. Dies hat auch nicht die oben erwähnte "Skalierung" -Problematik.Da gibt es immer nur zwei Ergebnisse für etwas, das ein boolesches Argument hat. Das Implementieren von zwei Methoden erfordert nur sehr wenig zusätzlichen Aufwand.
Weniger Code ist selten besser.
Mit diesem wird gesagt, es gibt einige Fälle , in denen ausdrücklich das Argument Lesbarkeit verbessert erklärt. Betrachten Sie zum Beispiel.
GetLatestNews(max: 10)
.GetLatestNews(10)
ist immer noch ziemlich selbsterklärend, die explizite Erklärung vonmax
wird helfen, alle aufzuklären Verwirrung.Und wenn Sie unbedingt ein boolesches Argument haben müssen, kann die Verwendung nicht durch einfaches Lesen von
true
oder abgeleitet werdenfalse
. Dann würde ich sagen:.. ist absolut besser und lesbarer als:
Da im zweiten Beispiel das
true
absolut alles bedeuten könnte . Aber ich würde versuchen, dieses Argument zu vermeiden.quelle