Beide erzeugen einen Fehler, der besagt, dass es sich um eine Konstante zur Kompilierungszeit handeln muss:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Kann jemand zunächst erklären, warum diese Werte beim Kompilieren nicht ermittelt werden können? Und gibt es eine Möglichkeit, einen Standardwert für ein optionales TimeSpan-Objekt anzugeben?
c#
c#-4.0
default-value
timespan
optional-parameters
Mike Pateras
quelle
quelle
new TimeSpan(2000)
dies nicht 2000 Millisekunden bedeutet, sondern 2000 "Ticks", was 0,2 Millisekunden oder eine 10.000stel von zwei Sekunden entspricht.Antworten:
Sie können dies sehr einfach umgehen, indem Sie Ihre Signatur ändern.
Ich sollte näher darauf eingehen - der Grund, warum diese Ausdrücke in Ihrem Beispiel keine Konstanten zur Kompilierungszeit sind, liegt darin, dass der Compiler zur Kompilierungszeit TimeSpan.FromSeconds (2.0) nicht einfach ausführen und die Bytes des Ergebnisses in Ihren kompilierten Code einfügen kann.
Überlegen Sie beispielsweise, ob Sie stattdessen DateTime.Now verwenden möchten. Der Wert von DateTime.Now ändert sich bei jeder Ausführung. Oder nehmen wir an, dass TimeSpan.FromSeconds die Schwerkraft berücksichtigt. Es ist ein absurdes Beispiel, aber die Regeln für Konstanten zur Kompilierungszeit machen keine Sonderfälle, nur weil wir zufällig wissen, dass TimeSpan.FromSeconds deterministisch ist.
quelle
<param>
, da er in der Signatur nicht sichtbar ist.span = span ?? TimeSpan.FromSeconds(2.0);
mit dem nullbaren Typ im Methodenkörper verwendet werden. Odervar realSpan = span ?? TimeSpan.FromSeconds(2.0);
um eine lokale Variable zu erhalten, die nicht nullwertfähig ist.Mein VB6-Erbe macht mich unruhig mit der Idee, "Nullwert" und "fehlender Wert" als gleichwertig zu betrachten. In den meisten Fällen ist dies wahrscheinlich in Ordnung, aber Sie haben möglicherweise eine unbeabsichtigte Nebenwirkung oder schlucken eine Ausnahmebedingung (z. B. wenn die Quelle
span
eine Eigenschaft oder Variable ist, die nicht null sein sollte, aber ist).Ich würde daher die Methode überladen:
quelle
Das funktioniert gut:
void Foo(TimeSpan span = default(TimeSpan))
quelle
TimeSpan
Werte haben soll, z. B. die vonnew TimeSpan(2000)
.Die Werte, die als Standardwert verwendet werden können, sind dieselben wie für ein Attributargument. Der Grund dafür ist, dass Standardwerte in Metadaten innerhalb von codiert werden
DefaultParameterValueAttribute
.Warum kann es zur Kompilierungszeit nicht ermittelt werden? Der Satz von Werten und Ausdrücken über solchen Werten, die zum Zeitpunkt der Kompilierung zulässig sind, ist in der offiziellen C # -Sprachenspezifikation aufgeführt :
Der Typ
TimeSpan
passt in keine dieser Listen und kann daher nicht als Konstante verwendet werden.quelle
TimeSpan
kann passen, der letzte auf dieser Listedefault(TimeSpan)
ist gültig.unter der Voraussetzung
default(TimeSpan)
ist kein gültiger Wert für die Funktion.Oder
unter der Voraussetzung
new TimeSpan()
ist kein gültiger Wert.Oder
Dies sollte besser sein, da die Wahrscheinlichkeit, dass der
null
Wert ein gültiger Wert für die Funktion ist, selten ist.quelle
TimeSpan
ist ein Sonderfall fürDefaultValueAttribute
und wird mit einer beliebigen Zeichenfolge angegeben, die über dieTimeSpan.Parse
Methode analysiert werden kann.quelle
Mein Vorschlag:
Übrigens
TimeSpan.FromSeconds(2.0)
ist nicht gleichnew TimeSpan(2000)
- der Konstruktor nimmt Ticks.quelle
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 Standardwertnull
dafür angeben. Beispielsweise:Verwenden Sie zum Aufrufen diese seltsame Syntax, um Eigenschaften in einem Ausdruck zu instanziieren und zuzuweisen:
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.
quelle