2016-12-19 4 views
0

Ich schreibe eine Methode (nennen wir es 'Bar'), die viele Parameter akzeptiert. Aus Gründen der Bequemlichkeit habe ich einen Standardwert festgelegt, so dass Entwickler beim Aufruf meiner Methode keinen Haufen von 'null' angeben müssen. Mit anderen Worten, sieht meine Methode Unterschrift wie folgt aus:Wie unterscheidet man zwischen ausgelassenen Parameter und Parameter Standardwert

public void Bar(string paramA = null, string paramB = null, ... many more parameters omitted...) 

Der Code in meiner Methode überprüft, für einen Nullwert und ignoriert den Parameter in diesem Fall. Es gibt jedoch einige Situationen, in denen ich zwischen einem Standard-Nullwert und einem vom Entwickler absichtlich gesetzten Nullwert unterscheiden muss.

Hier sind zwei Beispielanrufe zu illustrieren, worüber ich spreche. Die erste enthält alle Parameter (und daher wird der C# -Compiler sie zur Kompilierungszeit durch null ersetzen), und im zweiten Beispiel hat der Entwickler entschieden, für den ersten Parameter 'null' anzugeben.

Bar(); 
Bar(null); 

Ich habe mehrere Artikel gefunden, die über die Verwendung von „Option/Einige/Keine“ Muster sprechen. Das beste ist meiner Meinung nach here.

Aber ich kämpfe, um herauszufinden, wie ich immer noch einen "Standard" -Wert für die Bequemlichkeit der Entwickler bereitstellen kann. Konzeptionell, ich möchte so etwas schreiben:

public void Bar(Option<string> paramA = Option.None) 

aber der C# Compiler beschwert sich, dass ‚Option.None‘ ist keine Kompilierung-Konstante.

+2

Wenn Sie haben, „viele Parameter“, gibt es sehr oft eine Parameterklasse aussteigen zu kämpfen, dass Sie mit Eigenschaften nach Belieben initialisieren können (und die Sie auf die Standardwerte nicht zuweisen links). Dies erfordert auch keine funky 'Option' Klassen. Und es wird nicht brechen, wenn Sie die Standardeinstellungen in der nächsten Version ändern müssen. –

+0

Oder Sie können einen zusätzlichen Parameter 'bool useA = true' hinzufügen und wenn der Benutzer nicht A verwenden möchte, kann er' useA' auf 'false' setzen. In der Praxis ist es jedoch besser, eine Überladung hinzuzufügen oder eine Klasse als Parameter zu verwenden, wie es von anderen vorgeschlagen wurde. – Phil1970

Antwort

1

Das ist im Wesentlichen, was Nullable ist. Obwohl es für Strukturen verwendet wird, könnten Sie Ihre eigene Wrapper-Klasse erstellen, die dasselbe tut (mit Cast-Operatoren und ähnlichem).

Zum Beispiel könnten Sie Argument <T> erstellen, das aussieht und sich wie ein T verhält, aber eine Eigenschaft namens Set hat, die ein Bool zurückgibt.

Dann könnten Sie Ihre Methode definieren:

public void Bar(Argument<string> paramA = null, ...) 

Hier ist ein vollständiges Beispiel, aber ich muss zugeben, dass ich nicht mit ihm glücklich bin. Es scheint, dass ich den impliziten Cast-Operator nicht dazu bringen kann, geehrt zu werden, wenn er als Argument übergeben wird. Ich hatte gedacht, dass C# diese Art von Zwang vor ein paar Jahren hinzugefügt hat, als sie kovariante und kontravariante Würfe gemacht haben. Vielleicht habe ich mich falsch erinnert. Es erfordert also immer noch eine Besetzung (was ich durch eine Erweiterungsmethode mache).

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Calling Foo(default(string).ToArg())..."); 
     Foo(default(string).ToArg()); 

     Console.WriteLine("Calling Foo(((string)null).ToArg())..."); 
     Foo(((string)null).ToArg()); 

     Console.WriteLine("Calling Foo(\"test\".ToArg())..."); 
     Foo("test".ToArg()); 

     Console.WriteLine("Calling Foo()..."); 
     Foo(); 
     Console.ReadLine(); 
    } 

    public static void Foo(Argument<string> arg1 = null) 
    { 
     Console.WriteLine("arg1 is {0}null", arg1 == null ? "" : "not "); 
     Console.WriteLine("arg1.IsSet={0}", arg1?.IsSet ?? false); 
     Console.WriteLine("arg1.Value={0}", arg1?.Value ?? "(null)"); 
    } 

} 

public class Argument<T> 
{ 
    public Argument() 
    { 
     IsSet = false; 
    } 

    public Argument(T t) 
    { 
     _t = t; 
     IsSet = true; 
    } 

    private T _t; 

    public T Value { get { return _t; } set { _t = value; IsSet = true; } } 
    public bool IsSet { get; private set; } 
    public static implicit operator T(Argument<T> t) { return t._t; } 

} 

public static class Extensions 
{ 
    public static Argument<string> ToArg(this string s) { return new Argument<string>(s); } 

} 
-2

Je nach Art, würden Sie so etwas tun:

public static void Bar(string a = "", int b = 0) { 

, weil sie beide keinen Wert sind, aber immer noch auf NULL festlegbare optional. Für boolesche Werte würde ich einfach auf false setzen, da boolesche Werte keine Nullwerte haben. so:

public static void Bar(string a = "", int b = 0) { 
if (a == "") { 
// if there is no parameter 
} 
else { 
// stuff 
} 
if (b == 0) { 
// no param 
} 
else { 
// stuff 
} 

Hoffnung half ich!

+0

Es tut mir leid, ich sehe nicht, wie Ihr Vorschlag mir hilft, zwischen einem ausgelassenen Parameter und einem Parameter, der absichtlich auf null gesetzt wurde, zu unterscheiden. – desautelsj

2

C# hat das Konzept von "Überladungen", bei denen es sich um C# -Methoden mit demselben Namen, aber unterschiedlichen Parametern handelt.

private void MethodA(string param1) { 

} 

private void MethodA(string param1, string param2) { 

} 

Wenn dies nicht das ist, was Sie wollen, können Sie auch das Builder-Muster verwenden. Die Builder könnte eine Struktur sein, um Speicherzuweisungen zu reduzieren, wenn Sie sich über diese Art von Sache sorgen.

var result = new Builder() 
    .SetParam1(value) 
    .SetParam2(value) 
    .Build(); 
+0

Leider habe ich zu viele Parameter, um Überladungen zu berücksichtigen. Die Anzahl der Überlastungen wäre nicht beherrschbar. Ich muss jedoch in Ihren "Builder" Vorschlag schauen. – desautelsj

2

Nein, es gibt keine Möglichkeit, sie zu unterscheiden. In der Tat ist der "default" Wert in den Anruf durch den Compiler gebacken, so

Bar(); 

und

Bar(null); 

Ergebnis in äquivalenten IL.

Normalerweise würde ich Überladungen anstelle von Standardwerten empfehlen, aber da Sie mehrere Parameter haben, würde die Anzahl der Überlast exponentiell wachsen.

Wenn Sie zwischen null und einem "Standard" -Wert unterscheiden müssen, müssen Sie etwas anderes als Standard verwenden.

Eine andere Option wäre, einen Typ zu erstellen, der alle Parameterwerte speichern könnte. Dann können Sie feststellen, ob ein "Parameter" auf Null gesetzt wurde, anstatt standardmäßig Null zu sein, da Sie sich in den Eigenschaften-Setter einhaken können.

+0

Sie haben wahrscheinlich recht: Meine beste Option ist wahrscheinlich eine Klasse, um alle Parameter zu speichern – desautelsj

Verwandte Themen