Comment puis-je par défaut un paramètre sur Guid.Empty en C #?

178

Je souhaite dire:

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

Mais le compilateur se plaint que Guid.Empty n'est pas une constante de temps de compilation.

Comme je ne souhaite pas changer l'API, je ne peux pas utiliser:

 Nullable<Guid>
Ian Ringrose
la source
Quel est le problème avec le passage à Nullable<Guid> optional = null(ou plus brièvement Guid? optional = null)? Tous les Guids qui lui sont actuellement transmis seront forcés sans qu'aucune modification de code ne soit nécessaire.
NH.

Réponses:

235

Solution

Vous pouvez utiliser à la new Guid()place

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

Vous pouvez aussi utiliser default(Guid)

default(Guid)fonctionnera également exactement comme new Guid().

Étant donné que Guid est un type valeur et non un type référence, il default(Guid)n'est donc pas égal à, nullpar exemple, il est égal à l'appel du constructeur par défaut.

Ce qui signifie que ceci:

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

C'est exactement le même que l'exemple original.

Explication

Pourquoi n'a pas Guid.Emptyfonctionné?

La raison pour laquelle vous obtenez l'erreur est qu'elle Emptyest définie comme:

public static readonly Guid Empty;

Donc, c'est une variable, pas une constante (définie comme static readonlynon comme const). Le compilateur ne peut avoir que des valeurs connues du compilateur comme valeurs par défaut des paramètres de méthode (non connues uniquement à l'exécution).

La cause racine est que vous ne pouvez pas avoir constde tout struct, à la différence enumpar exemple. Si vous essayez, il ne se compilera pas.

La raison est encore une fois que ce structn'est pas un type primitif.
Pour une liste de tous les types primitifs dans .NET, voir http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(notez que enumgénéralement hérite int, qui est une primitive)

Mais ce new Guid()n'est pas non plus une constante!

Je ne dis pas qu'il faut une constante. Il a besoin de quelque chose qui peut être décidé au moment de la compilation. Emptyest un champ, donc sa valeur n'est pas connue au moment de la compilation (seulement au tout début de l'exécution).

La valeur du paramètre par défaut doit être connue au moment de la compilation, qui peut être une constvaleur, ou quelque chose de défini à l'aide d'une fonctionnalité C # qui rend la valeur connue au moment de la compilation, comme default(Guid)ou new Guid()(qui est décidé au moment de la compilation pour structs car vous ne pouvez pas modifier le structconstructeur dans code).

Bien que vous puissiez fournir defaultou newfacilement, vous ne pouvez pas fournir un const(car ce n'est pas un type primitif ou un enumcomme expliqué ci-dessus). Donc, encore une fois, ne pas dire que le paramètre facultatif lui-même a besoin d'une constante, mais une valeur connue du compilateur.

Meligy
la source
5
Eh bien, il n'a pas besoin d'une expression constante normale - new Guid()n'est pas une expression constante, par exemple. La spécification C # définit assez clairement ce qui est autorisé, y compris, mais sans s'y limiter, les constantes. (Juste pour être clair, il s'agit en fait d' une constante de temps de compilation, mais pas d'une "expression constante" en termes de spécification C #).
Jon Skeet
3
Lisez la partie Explication dans ma réponse. Ajouté pour répondre à cette partie.
Meligy
Belle explication Merci!
Daryl
Vous ne pouvez utiliser que de defaultnos jours :)
Joshit
151

Guid.Emptyest équivalent à new Guid(), qui est équivalent à default(Guid). Vous pouvez donc utiliser:

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

ou

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

Notez que la new Foo()valeur est uniquement applicable lorsque:

  • Vous appelez vraiment le constructeur sans paramètre
  • Foo est un type valeur

En d'autres termes, lorsque le compilateur sait qu'il s'agit en réalité de la valeur par défaut du type :)

( Fait intéressant, je suis 99,9% sûr que ce ne sera pas appeler de coutume new Foo()constructeur que vous avez créé. Vous ne pouvez pas créer un tel constructeur dans un type de valeur en C #, mais vous pouvez le faire en IL.)

Vous pouvez utiliser l' default(Foo)option pour n'importe quel type.

Jon Skeet
la source
Maintenant, pourquoi le message d'erreur du compilateur ne me dit-il pas cela, le complicateur pourrait vérifier le cas de Guid.Empty et donner un message plus utile.
Ian Ringrose
4
@Ian Ringrose: Je ne pense pas que le compilateur devrait avoir des messages spécifiques au type en général, pour être honnête.
Jon Skeet
2
La définition d'un paramètre par défaut sur un nouvel objet crée un nouvel objet à chaque fois que la méthode est appelée en PHP; mais ne crée qu'un seul objet pour l' ensemble du programme en Python. Vraiment, je considère que c'est l'un des très rares défauts de conception de Python . Je suis un peu heureux que C # (et VB.Net) aient évité ce problème en interdisant simplement les nouveaux objets dans les paramètres par défaut ... bien qu'il y ait des moments où cette capacité est vraiment agréable en PHP.
BlueRaja - Danny Pflughoeft
1
quelle pourrait être la raison pour laquelle cette même chose ne fonctionnerait pas dans l'action du contrôleur ASP.NET MVC? Tous les autres paramètres facultatifs fonctionnent (int, chaîne) mais cela ne fonctionne pas pour GUID, dit "Erreur de serveur dans l'application '/'. Le dictionnaire de paramètres contient une entrée nulle pour le paramètre 'categoryId' de type non nullable 'System.Guid '.. "Le code se compile correctement avec les deux spécifications (par défaut (Guid) et avec le nouveau Guid ()) mais émet cette erreur.
jument
@mare: Je ne sais pas, j'ai peur. Une autre option serait d'utiliser Nullable<Guid>, potentiellement.
Jon Skeet
18

Ne pouvez-vous pas utiliser:

default ( Guid ) ?

pseudo
la source
1
Non. Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
récursif
4
Désolé, je ne voulais pas dire ?? comme opérateur mais comme point d'interrogation accentué - je vais éditer!
Nick
9

La réponse acceptée ne fonctionne pas dans ASP.NET MVC et provoque cette erreur d'exécution:

[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)' ....

Au lieu de cela, vous pouvez effectuer les opérations suivantes:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
la source
Je vois la raison du vote négatif: la question spécifie que l'API ne peut pas être modifiée pour utiliser Nullable <Guid> - assez juste
Majix
À moins que vous ne souhaitiez attribuer explicitement le paramètre «facultatif» avec une valeur Guid vide, je pense que c'est la manière la plus naturelle de définir un paramètre facultatif de type Guid.
Gonzalo Méndez
4

Le compilateur est tout à fait correct; Guid.Emptyn'est pas une constante de compilation. Vous pouvez essayer de créer une surcharge de méthode comme celle-ci:

public void Problem()
{
    Problem(Guid.Empty);
}
un CVn
la source
J'ai dit que je ne souhaitais pas changer l'API, la méthode que j'essaie d'apprivoiser a plus de 10 paramètres!
Ian Ringrose
@Ian Ringrose, bien que je sois d'accord avec Guid x = default(Guid)la solution, rappelez-vous que l'ajout d'une autre surcharge de fonction ne complique pas plus l'API que l'ajout d'un argument facultatif. C'est vraiment ce que fait un argument optionnel de toute façon.
tenfour
@tenfour, il faudrait beaucoup de nouvelles surcharges de fonctions pour faire la même chose que 10 paramètres optionnels!
Ian Ringrose
Si vous avez une fonction publique qui nécessite plus de dix paramètres, peut-être que rendre un seul argument facultatif n'est pas vraiment une solution ... De plus, en revenant à la question d'origine, vous dites en effet que vous ne voulez pas "changer le API "(et comme le souligne Tenfour, la différence entre une surcharge explicite et un argument optionnel est minime en pratique) mais il n'y a aucune mention dans la question de la liste de paramètres étant ce genre de monstre.
un CVn le
En fait, c'est une réponse parfaite au problème.
PKD