2010-11-23 5 views
7

Ich habe gerade hinzugefügt aus bool Parameter zu einer Methode, die ich geschrieben habe, um eine Warnung in meine UI zu bekommen. Ich habe ein out verwendet, anstatt die Methode selbst zu bekommen, falsch/wahr zurückzugeben, da dies die DoSomething failed/succeed bedeuten würde. Mein Denken war, dass die warnUser zeigen würde, was die Warnung tatsächlich war, ohne auf die Implementierung der Methode zu schauen.Verwendet "out" schlechte Praxis

Originalcode

public void DoSomething(int id, string input); 

Neuer Code

public void DoSomething(int id, string input, out bool warnUser); 

ich Moq mit diesem Code zu testen, aber es bietet keine Unterstützung/ref Parameter aus, weil sie nicht unterstützt von Lambda-Ausdrücken

Testcode

mockService.Verify(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<bool>()); 

So wird mit aus Parametern schlechte Praxis und wenn ja, was soll ich tun, statt?

+1

Wie zeigt ein 'bool' an, was die Warnung tatsächlich war?" –

+0

@Cody - weil es ein aussagekräftiger Name in meinem "echten" Code ist :) –

Antwort

9

stoppt einen out-Parameter in einem Hohlraum Methode ist im Allgemeinen eines schlechte Idee. Sie sagen, Sie haben es verwendet, "anstatt die Methode selbst dazu zu bringen, falsch/wahr zurückzugeben, da dies bedeuten würde, dass die DoSomething gescheitert ist/gelungen ist" - ich glaube nicht, dass diese Implikation vorhanden ist. In .NET wird der Fehler normalerweise durch eine Ausnahme und nicht durch true/false angezeigt.

out Parameter sind in der Regel hässliche Rückgabewerte zu verwenden, als - insbesondere eine Variable des richtigen Typs zu handhaben haben, so dass Sie nicht nur schreiben:

if (DoSomething(...)) 
{ 
    // Warn user here 
} 

Alternative Man könnten Sie Möchten Sie in Betracht ziehen, ist eine Aufzählung, die die erforderliche Warnstufe angibt. Zum Beispiel:

public enum WarningLevel 
{ 
    NoWarningRequired, 
    WarnUser 
} 

Dann könnte die Methode zurückgeben WarningLevel statt bool. Das würde deutlicher machen, was Sie meinten - obwohl Sie die Dinge vielleicht etwas umbenennen möchten. (Es ist schwer, Ratschläge mit metasyntaktischen Namen wie "DoSomething" zu geben, obwohl ich völlig verstehe, warum Sie das hier benutzt haben.)

Natürlich können Sie auch andere Informationen haben - wie die Grund für die Warnung. Das könnte mit einem Enum gemacht werden, oder Sie möchten vielleicht etwas reicheres Ergebnis geben.

+0

Ich bin nicht einverstanden über die Auswirkungen des Scheiterns. Wenn eine Methode eine Bool-Rückgabe hat, bedeutet das für mich, dass eine Rückgabe von false bedeutet, dass sie fehlgeschlagen ist. Aber ich stimme zu, dass ein Fehler im Allgemeinen durch eine Ausnahme (vorzugsweise eine stark typisierte Ausnahme) angezeigt wird. Es mag die enum-Idee, obwohl ich das selbst erkannt hätte, da ich diesen Ansatz in der Vergangenheit benutzt habe. Vielen Dank. –

+0

+1 für den 'WarningLevel'-Vorschlag. Dies würde die Verwirrung beseitigen, über die der Fragesteller besorgt ist, da der Rückgabewert als Erfolg oder Misserfolg der Funktion interpretiert wird. –

+1

Ich habe diese Antwort akzeptiert, da mein Anrufcode viel besser lesbar ist. "if (! DoSomething (...)" vs "if (DoSomething (...) == WarningLevel.WarnUser)" Natürlich hätte ich etwas Sinnvolleres als "WarnUser" –

3

Ein paar zusätzliche Gedanken:

  • Was FxCop sagt: CA1021: Avoid out parameters

  • Für private/interne Methoden (Implementierungsdetails) out/ref Parameter sind weniger ein Problem

  • C# 7 Tuples oft sind eine bessere Alternative zu out Parameter

  • Ferner verbessert C# 7 den Umgang mit out Parametern auf der Website Aufruf von „out Variablen“ Einführung und indem sie zu „verwerfen“ out Parameter

0

Ich denke, dass der beste Weg, dies zu tun ist, zu verwenden, erwartete Ausnahmen. Sie können eine Reihe von benutzerdefinierten Ausnahmen erstellen, die Ihre Warnungen definieren, und sie in der aufrufenden Methode abfangen.

public class MyWarningException : Exception() {} 

... 

try 
{ 
    obj.DoSomething(id,input); 
} 
catch (MyWarningException ex) 
{ 
    // do stuff 
} 

Die Idee wäre, dass die Ausnahme am Ende der Umsetzung DoSomething angehoben wird, so dass der Fluss nicht in der Mitte

+5

Eine Ausnahme sollte nur ausgelöst werden, wenn die Methode nicht das tun kann, was IMO gemeint ist. Wenn es gelingt, aber aus irgendeinem Grund sollte der Benutzer eine Warnung erhalten, das klingt nicht wie eine außergewöhnliche Situation für mich. –

+0

Exception-System ist nützlich, aber wirklich langsam ... – ykatchou

+3

@ykatchou eigentlich ist es nicht * das * langsam; aber es ist keine gute Übung für den allgemeinen Logikfluss. –

7

out ist sehr viel ein nützliches Konstrukt, insbesondere in Mustern wie bool TryGetFoo(..., out value), wo Sie die „if“ und das „Was“ getrennt wissen wollen (und nullable ist nicht unbedingt eine Option).

jedoch - in diesem Fall, warum nicht einfach machen:

public bool DoSomething(int id, string input); 

und den Rückgabewert verwenden, um dies zu signalisieren?

+1

Wie gesagt, ich nicht möchte andeuten, dass das "Etwas" fehlgeschlagen ist/erfolgreich –

+2

@Marc Gravell Ich würde argumentieren, dass in .NET 4, mit einem Tuple klarer sein könnte, wie die Eingabe (Methode Argumente) von der Ausgabe getrennt ist (zurück Werte und liefert immer noch das "Wenn" und "Was". Und natürlich funktionieren Tupel gut in Lambdas. –

+2

@Antony - Ich bin mir nicht sicher, ich sehe die Bedeutung dieses Kommentars; Sie * * * jedoch scheinen um genau einen Wert zurück zu bekommen - warum sollte 'out' verwendet werden, wenn du 'return' dafür verwenden kannst? –

0

Dies ist eine wirklich schwierige Frage zu beantworten, ohne mehr über den Kontext zu wissen.

Wenn DoSomething ist eine Methode in der UI-Schicht, die etwas tun, die UI verwandt ist, dann ist es vielleicht in Ordnung. Wenn DoSomething eine Business-Schicht-Methode ist, dann ist dies wahrscheinlich kein guter Ansatz, weil es bedeutet, dass die Business-Schicht verstehen muss, was eine angemessene Antwort sein könnte, und sie muss möglicherweise sogar auf Lokalisierungsprobleme achten.

Aus rein subjektiver Sicht habe ich mich von Out-Parametern fern gehalten. Ich denke, sie stören den Fluss des Codes und machen es etwas weniger klar.

0

Ich denke, die beste beste Lösung für Ihren Fall ist die Verwendung eines Delegaten anstelle von Bool. Verwenden Sie etwas wie folgt:

Sie können eine leere Lamda weitergeben, wo Sie Warnungen an den Benutzer nicht anzeigen möchten.

1

Angesichts einer großen Legacy-Code-Methode (sagen 15 Zeilen, Legacy-Code-Methoden mit mehr als 200 Zeilen sind ziemlich häufig), könnte man die "Extract Method" Refactoring verwenden, um Code weniger spröde und wartungsfreundlicher zu machen .

Ein solches Refactoring führt sehr wahrscheinlich zu einer oder mehreren neuen Methoden mit out-Parametern für die Einstellung von lokalen Variablenwerten für die vorherige Methode.

Persönlich verwende ich diesen Umstand für einen starken Hinweis, dass es eine oder mehrere diskrete Klassen in der früheren Methode versteckt hat, so dass ich "Klasse extrahieren" kann, wo in der neuen Klasse diese out-Parameter Eigenschaften sichtbar machen an die aufrufende (vorherige) Methode.

Ich halte es für eine schlechte Praxis, die extrahierten Methoden ohne Parameter in der früheren Methode zu belassen und den kohärenten folgenden Schritt von "Klasse extrahieren" zu überspringen, weil diese lokalen Variablen in Ihrer früheren Funktion verbleiben, aber tun semantisch gehören nicht mehr dazu.

Also, in einem solchen Szenario, out Parameter sind meiner Meinung nach ein Code, der SRP bricht.