Andere Antwort haben großartige Erklärungen gegeben, warum ein optionaler Parameter kein dynamischer Ausdruck sein kann. Um es noch einmal zu erzählen, verhalten sich Standardparameter wie Kompilierungszeitkonstanten. Das bedeutet, dass der Compiler sie auswerten und eine Antwort finden muss. Es gibt einige Leute, die möchten, dass C # den Compiler unterstützt, der dynamische Ausdrücke auswertet, wenn er auf konstante Deklarationen stößt. Diese Art von Funktion würde sich auf Markierungsmethoden als „rein“ beziehen, aber das ist derzeit keine Realität und wird es möglicherweise nie sein.
Eine Alternative zur Verwendung eines C # -Standardparameters für eine solche Methode wäre die Verwendung des durch beispielhaft dargestellten Musters XmlReaderSettings
. Definieren Sie in diesem Muster eine Klasse mit einem parameterlosen Konstruktor und öffentlich beschreibbaren Eigenschaften. Ersetzen Sie dann alle Optionen durch Standardeinstellungen in Ihrer Methode durch ein Objekt dieses Typs. Machen Sie dieses Objekt sogar optional, indem Sie einen Standardwert null
dafür angeben. Beispielsweise:
public class FooSettings
{
public TimeSpan Span { get; set; } = TimeSpan.FromSeconds(2);
// I imagine that if you had a heavyweight default
// thing you’d want to avoid instantiating it right away
// because the caller might override that parameter. So, be
// lazy! (Or just directly store a factory lambda with Func<IThing>).
Lazy<IThing> thing = new Lazy<IThing>(() => new FatThing());
public IThing Thing
{
get { return thing.Value; }
set { thing = new Lazy<IThing>(() => value); }
}
// Another cool thing about this pattern is that you can
// add additional optional parameters in the future without
// even breaking ABI.
//bool FutureThing { get; set; } = true;
// You can even run very complicated code to populate properties
// if you cannot use a property initialization expression.
//public FooSettings() { }
}
public class Bar
{
public void Foo(FooSettings settings = null)
{
// Allow the caller to use *all* the defaults easily.
settings = settings ?? new FooSettings();
Console.WriteLine(settings.Span);
}
}
Verwenden Sie zum Aufrufen diese seltsame Syntax, um Eigenschaften in einem Ausdruck zu instanziieren und zuzuweisen:
bar.Foo(); // 00:00:02
bar.Foo(new FooSettings { Span = TimeSpan.FromDays(1), }); // 1.00:00:00
bar.Foo(new FooSettings { Thing = new MyCustomThing(), }); // 00:00:02
Nachteile
Dies ist ein wirklich schwerer Ansatz zur Lösung dieses Problems. Wenn Sie eine schnelle und fehlerhafte interne Schnittstelle schreiben und die TimeSpan
Nullbar machen und Null wie Ihren gewünschten Standardwert behandeln, funktioniert dies einwandfrei.
Wenn Sie eine große Anzahl von Parametern haben oder die Methode in einer engen Schleife aufrufen, hat dies auch den Aufwand für Klasseninstanziierungen. Wenn Sie eine solche Methode in einer engen Schleife aufrufen, kann es natürlich natürlich und sogar sehr einfach sein, eine Instanz des FooSettings
Objekts wiederzuverwenden .
Leistungen
Wie ich im Kommentar im Beispiel erwähnt habe, ist dieses Muster für öffentliche APIs ideal. Das Hinzufügen neuer Eigenschaften zu einer Klasse ist eine nicht unterbrechende ABI-Änderung. Sie können also neue optionale Parameter hinzufügen, ohne die Signatur Ihrer Methode mithilfe dieses Musters zu ändern. So erhalten Sie neueren kompilierten Code mehr Optionen, während Sie weiterhin alten kompilierten Code ohne zusätzliche Arbeit unterstützen .
Da die in C # integrierten Standardmethodenparameter als Kompilierungszeitkonstanten behandelt und in die Aufrufseite eingebrannt werden, werden Standardparameter nur dann vom Code verwendet, wenn sie neu kompiliert werden. Durch Instanziieren eines Einstellungsobjekts lädt der Aufrufer beim Aufrufen Ihrer Methode dynamisch die Standardwerte. Dies bedeutet, dass Sie die Standardeinstellungen aktualisieren können, indem Sie einfach Ihre Einstellungsklasse ändern. Auf diese Weise können Sie Standardwerte ändern, ohne Anrufer neu kompilieren zu müssen, um die neuen Werte anzuzeigen, falls dies gewünscht wird.
new TimeSpan(2000)
dies nicht 2000 Millisekunden bedeutet, sondern 2000 "Ticks", was 0,2 Millisekunden oder eine 10.000stel von zwei Sekunden entspricht.