Wie kann ich einen Parameter in C # standardmäßig auf Guid.Empty setzen?

177

Ich möchte sagen:

public void Problem(Guid optional = Guid.Empty)
{
}

Der Compiler beschwert sich jedoch, dass Guid.Empty keine Kompilierungszeitkonstante ist.

Da ich die API nicht ändern möchte, kann ich nicht verwenden:

 Nullable<Guid>
Ian Ringrose
quelle
Was ist falsch daran, zu Nullable<Guid> optional = null(oder knapper Guid? optional = null) zu wechseln ? Alle Guids, die derzeit an sie übergeben werden, werden gezwungen, ohne dass Codeänderungen erforderlich sind.
NH.

Antworten:

234

Lösung

Sie können new Guid()stattdessen verwenden

public void Problem(Guid optional = new Guid())
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Sie können auch verwenden default(Guid)

default(Guid)wird auch genau so funktionieren new Guid().

Da Guid ein Werttyp ist, der kein Referenztyp default(Guid)ist, entspricht er nullbeispielsweise nicht dem Aufruf des Standardkonstruktors.

Was bedeutet, dass dies:

public void Problem(Guid optional = default(Guid))
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Es ist genau das gleiche wie im Originalbeispiel.

Erläuterung

Warum hat es nicht Guid.Emptyfunktioniert?

Der Grund, warum Sie den Fehler erhalten, ist Emptyfolgender:

public static readonly Guid Empty;

Es ist also eine Variable, keine Konstante (definiert als static readonlynicht als const). Der Compiler kann nur vom Compiler bekannte Werte als Standardwerte für Methodenparameter haben (nicht nur zur Laufzeit bekannt).

Die Hauptursache ist, dass Sie keine haben constkönnen struct, anders als enumzum Beispiel. Wenn Sie es versuchen, wird es nicht kompiliert.

Der Grund ist noch einmal, dass structes sich nicht um einen primitiven Typ handelt.
Eine Liste aller primitiven Typen in .NET finden Sie unter http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(beachten Sie, dass enumnormalerweise geerbt wird int, was ein primitiver Typ ist).

Ist new Guid()aber auch keine Konstante!

Ich sage nicht, dass es eine Konstante braucht. Es braucht etwas, das in der Kompilierungszeit entschieden werden kann. Emptyist ein Feld, daher ist sein Wert in der Kompilierungszeit nicht bekannt (nur zu Beginn der Laufzeit).

Der Standardparameterwert muss zur Kompilierungszeit bekannt sein. Dies kann ein constWert sein oder etwas, das mit einer C # -Funktion definiert wurde, die den Wert zur Kompilierungszeit bekannt macht, wie default(Guid)oder new Guid()(der zur Kompilierungszeit für structs festgelegt wird, da Sie den structKonstruktor in nicht ändern können Code).

Während Sie bereitstellen können defaultoder neweinfach, können Sie kein a bereitstellen const(da es sich nicht um einen primitiven Typ oder einen enumwie oben erläuterten handelt). Wenn Sie also nicht sagen, dass der optionale Parameter selbst einen konstanten, aber vom Compiler bekannten Wert benötigt.

Meligy
quelle
5
Nun, es braucht keinen normalen konstanten Ausdruck - new Guid()ist zum Beispiel kein konstanter Ausdruck. Die C # -Spezifikation definiert ganz klar, was erlaubt ist, einschließlich, aber nicht beschränkt auf Konstanten. (Just klar zu sein, ist es effektiv ist eine Kompilierung konstant, nur nicht ein „konstanter Ausdruck“ in C # spec Begriffe).
Jon Skeet
3
Lesen Sie den Teil Erklärung in meiner Antwort. Hinzugefügt, um diesen Teil zu beantworten.
Meligy
Schöne Erklärung. Danke!
Daryl
Sie können nur defaultheutzutage verwenden :)
Joshit
151

Guid.Emptyist äquivalent zu new Guid(), was äquivalent zu ist default(Guid). So können Sie verwenden:

public void Problem(Guid optional = default(Guid))

oder

public void Problem(Guid optional = new Guid())

Beachten Sie, dass der new Foo()Wert nur gilt, wenn:

  • Sie rufen wirklich den parameterlosen Konstruktor auf
  • Foo ist ein Werttyp

Mit anderen Worten, wenn der Compiler weiß, dass es wirklich nur der Standardwert für den Typ ist :)

(Interessanterweise bin ich zu 99,9% sicher, dass kein benutzerdefinierter new Foo()Konstruktor aufgerufen wird, den Sie möglicherweise erstellt haben. Sie können einen solchen Konstruktor nicht in einem Werttyp in C # erstellen, aber Sie können dies in IL tun.)

Sie können die default(Foo)Option für jeden Typ verwenden.

Jon Skeet
quelle
Warum sagt mir die Compiler-Fehlermeldung dies nicht? Der Complier könnte nach dem Fall von Guid.Empty suchen und eine hilfreichere Meldung geben.
Ian Ringrose
4
@ Ian Ringrose: Ich denke nicht, dass der Compiler generell typspezifische Nachrichten haben sollte, um ehrlich zu sein.
Jon Skeet
2
Wenn Sie einen Parameter auf den Standardwert für ein neues Objekt setzen, wird bei jedem Aufruf der Methode in PHP ein neues Objekt erstellt. Erstellt jedoch nur ein Objekt für das gesamte Programm in Python. Ich halte dies wirklich für einen der wenigen Designfehler von Python . Ich bin etwas froh, dass C # (und VB.Net) dieses Problem vermieden haben, indem sie einfach neue Objekte in Standardparametern nicht zugelassen haben ... obwohl es Zeiten gibt, in denen diese Fähigkeit in PHP wirklich gut ist.
BlueRaja - Danny Pflughoeft
1
Was könnte der Grund dafür sein, dass dasselbe in der ASP.NET MVC-Controller-Aktion nicht funktioniert? Alle anderen optionalen Parameter funktionieren (int, string), aber nicht für die GUID, sagt "Serverfehler in '/' Anwendung. Das Parameterwörterbuch enthält einen Nulleintrag für den Parameter 'categoryId' vom nicht nullbaren Typ 'System.Guid '.. "Der Code wird mit beiden Spezifikationen (Standard (Guid) und mit neuem Guid ()) einwandfrei kompiliert, gibt jedoch diesen Fehler aus.
Stute
@mare: Ich weiß nicht, ich fürchte. Eine andere Option wäre möglicherweise die Verwendung Nullable<Guid>.
Jon Skeet
18

Kannst du nicht benutzen:

default ( Guid ) ?

Nick
quelle
1
Nein Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
rekursiv
4
Entschuldigung, ich meinte nicht? als Operator, aber als hervorgehobenes Fragezeichen - ich werde bearbeiten!
Nick
9

Die akzeptierte Antwort funktioniert in ASP.NET MVC nicht und verursacht diesen Laufzeitfehler:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'optional' of non-nullable type 'System.Guid' for method 'System.Web.Mvc.ActionResult Problem(System.Guid)' ....

Stattdessen können Sie Folgendes tun:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
quelle
Ich sehe den Grund für das Down-Voting: Die Frage gibt an, dass die API nicht geändert werden kann, um Nullable <Guid> zu verwenden - fair genug
Majix
Sofern Sie den Parameter "optional" nicht explizit mit einem leeren Guid-Wert zuweisen möchten, ist dies meiner Meinung nach die natürlichste Methode zum Definieren eines optionalen Parameters vom Typ Guid.
Gonzalo Méndez
4

Der Compiler ist ganz richtig; Guid.Emptyist keine Kompilierungszeitkonstante. Sie können versuchen, eine Methodenüberladung wie folgt durchzuführen:

public void Problem()
{
    Problem(Guid.Empty);
}
ein CVn
quelle
Ich sagte, ich wollte die API nicht ändern, die Methode, die ich zu zähmen versuche, hat über 10 Parameter!
Ian Ringrose
@ Ian Ringrose, obwohl ich Guid x = default(Guid)als Lösung damit einverstanden bin , denken Sie daran, dass das Hinzufügen einer weiteren Funktionsüberladung die API nicht mehr kompliziert als das Hinzufügen eines optionalen Arguments. Das ist es wirklich, was ein optionales Argument sowieso tut.
Tenfour
@tenfour, es müssten viele neue Funktionsüberladungen sein, um dasselbe zu tun wie 10 optionale Parameter!
Ian Ringrose
Wenn Sie eine öffentliche Funktion haben, für die mehr als zehn Parameter erforderlich sind, ist es möglicherweise nicht wirklich eine Lösung, ein einzelnes Argument optional zu machen. Wenn Sie auf die ursprüngliche Frage zurückblicken, sagen Sie tatsächlich, dass Sie das nicht ändern möchten API "(und wie Tenfour hervorhebt, ist der Unterschied zwischen einer expliziten Überladung und einem optionalen Argument in der Praxis minimal), aber es gibt keine Erwähnung in der Frage, ob die Parameterliste diese Art von Monster ist.
Ein CVn
Eigentlich ist dies eine perfekte Antwort auf das Problem.
PKD