Pourquoi «décimal» n'est pas un type de paramètre d'attribut valide?

139

C'est vraiment incroyable mais réel. Ce code ne fonctionnera pas:

[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field)]
public class Range : Attribute
{
    public decimal Max { get; set; }
    public decimal Min { get; set; }
}

public class Item
{
    [Range(Min=0m,Max=1000m)]  //compile error:'Min' is not a valid named attribute argument because it is not a valid attribute parameter type 
    public decimal Total { get; set; }  
}

Pendant que cela fonctionne:

[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field)]
public class Range : Attribute
{
    public double Max { get; set; }
    public double Min { get; set; }
}

public class Item
{
    [Range(Min=0d,Max=1000d)]
    public decimal Total { get; set; }  
}

Qui peut me dire pourquoi le double est OK alors que la décimale ne l'est pas.

Cheng Chen
la source

Réponses:

139

Il s'agit d'une restriction CLR. Seules les constantes primitives ou les tableaux de primitives peuvent être utilisés comme paramètres d'attribut. La raison en est qu'un attribut doit être entièrement codé en métadonnées. Ceci est différent d'un corps de méthode qui est codé en IL. L'utilisation de MetaData ne restreint que sévèrement la portée des valeurs qui peuvent être utilisées. Dans la version actuelle du CLR, les valeurs des métadonnées sont limitées aux primitives, nulles, aux types et aux tableaux de primitives (peut-être en avoir manqué une mineure).

Tiré de cette réponse par JaredPar .

Les décimales alors qu'un type de base ne sont pas un type primitif et ne peuvent donc pas être représentées dans les métadonnées, ce qui l'empêche d'être un paramètre d'attribut.

djdd87
la source
35
Pourquoi les décimales ne sont pas considérées comme des types primitifs dans le CLR?
koumides
10
@koumides je pense que la réponse est que le type est trop grand pour être exprimé dans un seul registre CPU car il est de 128 bits
Chris Marisic
2
OK alors pourquoi les chaînes sont-elles autorisées comme propriétés d'attribut? Je suppose que cela relève de la catégorie `` tableau de primitives '' mais il est alloué par tas (type de référence) ...
Steztric
Parce que les chaînes sont des types de référence qui sont traités de manière complètement différente.
Carsten Schütte
2
@Soren ce n'est pas vrai, Enumsont pris en charge. J'ai actuellement 2 attributs personnalisés l'un avec 2 énumérations et les autres avec un tableau d'énumérations.
Franck
60

D'après les spécifications :

Les types de paramètres positionnels et nommés pour une classe d'attributs sont limités aux types de paramètres d'attribut, qui sont:

  • L' un des types suivants: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • Le type object.
  • Le type System.Type.
  • Un type enum, à condition qu'il ait une accessibilité publique et que les types dans lesquels il est imbriqué (le cas échéant) ont également une accessibilité publique (spécification d'attribut).
  • Tableaux unidimensionnels des types ci-dessus.
Kobi
la source
10
Correct, mais notez que vous citez une ancienne version de la spécification. En C # versions 3.0, 4.0 et 5.0, il est dit qu'il peut aussi avoir le type sbyte, ushort, uint, ulong. Et cela semble bien fonctionner. Mais decimaln'est toujours pas autorisé :-(
Jeppe Stig Nielsen
1
@JeppeStigNielsen J'ai mis à jour le lien des spécifications et la citation
Ohad Schneider
6
Les primitives Nullable ne sont PAS non plus prises en charge.
KTCO
2

La réponse à ce problème est d'utiliser des chaînes, qui sont autorisées comme attributs bien qu'elles ne soient pas de type atomique. N'utilisez pas de doubles car l'arrondi rendra les résultats moins précis.

public String MinimumValue
{
    get
    {
        return minimumValueDecimal.ToString();
    }

    set
    {
        minimumValueDecimal = Decimal.Parse(value);
    }
}

private decimal minimumValueDecimal;
Daniel Barbalace
la source