Les deux génèrent une erreur indiquant qu'ils doivent être une constante de compilation:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Tout d'abord, quelqu'un peut-il expliquer pourquoi ces valeurs ne peuvent pas être déterminées au moment de la compilation? Et existe-t-il un moyen de spécifier une valeur par défaut pour un objet TimeSpan facultatif?
c#
c#-4.0
default-value
timespan
optional-parameters
Mike Pateras
la source
la source
new TimeSpan(2000)
cela ne signifie pas 2000 millisecondes, cela signifie 2000 "ticks", soit 0,2 millisecondes, soit un 10 000 ème de deux secondes.Réponses:
Vous pouvez contourner ce problème très facilement en modifiant votre signature.
Je devrais élaborer - la raison pour laquelle ces expressions dans votre exemple ne sont pas des constantes au moment de la compilation est qu'au moment de la compilation, le compilateur ne peut pas simplement exécuter TimeSpan.FromSeconds (2.0) et coller les octets du résultat dans votre code compilé.
À titre d'exemple, considérez si vous avez essayé d'utiliser DateTime.Now à la place. La valeur de DateTime.Now change chaque fois qu'elle est exécutée. Ou supposons que TimeSpan.FromSeconds prenne en compte la gravité. C'est un exemple absurde, mais les règles des constantes de compilation ne font pas de cas particuliers simplement parce que nous savons que TimeSpan.FromSeconds est déterministe.
la source
<param>
, car elle n'est pas visible dans la signature.span = span ?? TimeSpan.FromSeconds(2.0);
avec le type Nullable, dans le corps de la méthode. Ouvar realSpan = span ?? TimeSpan.FromSeconds(2.0);
pour obtenir une variable locale qui n'est pas nullable.Mon héritage VB6 me rend mal à l'aise avec l'idée de considérer «valeur nulle» et «valeur manquante» comme équivalentes. Dans la plupart des cas, c'est probablement bien, mais vous pourriez avoir un effet secondaire involontaire, ou vous pourriez avaler une condition exceptionnelle (par exemple, si la source de
span
est une propriété ou une variable qui ne devrait pas être nulle, mais qui l'est).Je surchargerais donc la méthode:
la source
Cela fonctionne bien:
void Foo(TimeSpan span = default(TimeSpan))
la source
TimeSpan
valeurs arbitraires , comme celle donnée parnew TimeSpan(2000)
.L'ensemble des valeurs qui peuvent être utilisées comme valeur par défaut sont les mêmes que celles qui peuvent être utilisées pour un argument d'attribut. La raison en est que les valeurs par défaut sont encodées en métadonnées à l'intérieur du
DefaultParameterValueAttribute
.Quant à savoir pourquoi il ne peut pas être déterminé au moment de la compilation. L'ensemble de valeurs et d'expressions sur ces valeurs autorisées au moment de la compilation est répertorié dans les spécifications officielles du langage C # :
Le type
TimeSpan
ne rentre dans aucune de ces listes et ne peut donc pas être utilisé comme constante.la source
TimeSpan
peut contenir le dernier de cette listedefault(TimeSpan)
est valide.fourni
default(TimeSpan)
n'est pas une valeur valide pour la fonction.Ou
fourni
new TimeSpan()
n'est pas une valeur valide.Ou
Cela devrait être mieux vu que les chances que la
null
valeur soit une valeur valide pour la fonction sont rares.la source
TimeSpan
est un cas particulier pour leDefaultValueAttribute
et est spécifié à l'aide de toute chaîne pouvant être analysée via laTimeSpan.Parse
méthode.la source
Ma suggestion:
BTW
TimeSpan.FromSeconds(2.0)
n'est pas égalnew TimeSpan(2000)
- le constructeur prend des graduations.la source
D'autres réponses ont donné de grandes explications sur les raisons pour lesquelles un paramètre facultatif ne peut pas être une expression dynamique. Mais, pour raconter, les paramètres par défaut se comportent comme des constantes de temps de compilation. Cela signifie que le compilateur doit être capable de les évaluer et de trouver une réponse. Il y a des gens qui veulent que C # ajoute la prise en charge du compilateur évaluant les expressions dynamiques lorsqu'il rencontre des déclarations constantes - ce type de fonctionnalité serait lié au marquage des méthodes «pures», mais ce n'est pas une réalité pour le moment et pourrait ne jamais l'être.
Une alternative à l'utilisation d'un paramètre par défaut C # pour une telle méthode serait d'utiliser le modèle illustré par
XmlReaderSettings
. Dans ce modèle, définissez une classe avec un constructeur sans paramètre et des propriétés publiquement accessibles en écriture. Remplacez ensuite toutes les options par les valeurs par défaut de votre méthode par un objet de ce type. Rendez même cet objet facultatif en spécifiant une valeur par défaut denull
for it. Par exemple:Pour appeler, utilisez cette syntaxe étrange pour instancier et attribuer des propriétés dans une seule expression:
Inconvénients
Il s'agit d'une approche très lourde pour résoudre ce problème. Si vous écrivez une interface interne rapide et sale et que vous
TimeSpan
rendez la valeur Nullable et que vous la traitez comme la valeur par défaut souhaitée, cela fonctionnerait bien, faites-le à la place.De plus, si vous avez un grand nombre de paramètres ou si vous appelez la méthode dans une boucle serrée, cela entraînera la surcharge des instanciations de classe. Bien sûr, si vous appelez une telle méthode dans une boucle serrée, il peut être naturel et même très facile de réutiliser une instance de l'
FooSettings
objet.Avantages
Comme je l'ai mentionné dans le commentaire de l'exemple, je pense que ce modèle est idéal pour les API publiques. L'ajout de nouvelles propriétés à une classe est un changement ABI sans rupture, vous pouvez donc ajouter de nouveaux paramètres facultatifs sans changer la signature de votre méthode à l'aide de ce modèle - donnant plus d'options au code compilé plus récemment tout en continuant à prendre en charge l'ancien code compilé sans travail supplémentaire .
De plus, étant donné que les paramètres de méthode par défaut intégrés de C # sont traités comme des constantes de compilation et intégrés au site d'appel, les paramètres par défaut ne seront utilisés par le code qu'une fois recompilé. En instanciant un objet de paramètres, l'appelant charge dynamiquement les valeurs par défaut lors de l'appel de votre méthode. Cela signifie que vous pouvez mettre à jour les valeurs par défaut en modifiant simplement votre classe de paramètres. Ainsi, ce modèle vous permet de modifier les valeurs par défaut sans avoir à recompiler les appelants pour voir les nouvelles valeurs, si cela est souhaité.
la source