Por que "decimal" não é um tipo de parâmetro de atributo válido?

139

É realmente inacreditável, mas real. Este código não funcionará:

[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; }  
}

Enquanto isso funciona:

[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; }  
}

Quem pode me dizer por que o dobro está OK e o decimal não.

Cheng Chen
fonte

Respostas:

139

Esta é uma restrição do CLR. Somente constantes primitivas ou matrizes de primitivas podem ser usadas como parâmetros de atributo. O motivo é que um atributo deve ser codificado inteiramente em metadados. Isso é diferente de um corpo de método que é codificado em IL. O uso de MetaData restringe apenas severamente o escopo dos valores que podem ser usados. Na versão atual do CLR, os valores de metadados são limitados a primitivos, nulos, tipos e matrizes de primitivos (pode ter faltado um menor).

Retirado desta resposta por JaredPar .

Os decimais enquanto um tipo básico não são primitivos e, portanto, não podem ser representados em metadados, o que impede que seja um parâmetro de atributo.

djdd87
fonte
35
Por que decimais não são considerados tipos primitivos no CLR?
Koumides
10
@koumides Creio que a resposta é o tipo é muito grande para expressar em um único registo CPU como é de 128 bits
Chris Marisic
2
OK, então por que as strings são permitidas como propriedades de atributo? Suponho que ele se
enquadre
Porque strings são tipos de referência que são manipulados completamente diferentes.
Carsten Schütte
2
@ Soren isso não é verdade, Enumsão suportados. Atualmente, tenho 2 atributos personalizados, um com 2 enumerações e os outros com uma matriz de enumeração.
Franck
60

Das especificações :

Os tipos de parâmetros posicionais e nomeados para uma classe de atributo são limitados aos tipos de parâmetro de atributo, que são:

  • Um dos seguintes tipos: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • O tipo object.
  • O tipo System.Type.
  • Um tipo de enumeração, desde que tenha acessibilidade pública e os tipos em que está aninhado (se houver) também tenham acessibilidade pública (especificação de atributo).
  • Matrizes unidimensionais dos tipos acima.
Kobi
fonte
10
Correto, mas observe que você está citando uma versão antiga da especificação. Em C # versões 3.0, 4.0 e 5.0, afirma-se que ele também pode ter tipo sbyte, ushort, uint, ulong. E isso parece funcionar bem. Mas ainda decimalnão é permitido :-(
Jeppe Stig Nielsen
1
@JeppeStigNielsen Eu atualizei o link especificação e cotação
Ohad Schneider
6
Primitivas anuláveis ​​também NÃO são suportadas.
KTCO 17/02
2

A resposta para esse problema é usar cadeias, que são permitidas como atributos, apesar de não serem do tipo atômico. Não use duplos, pois o arredondamento tornará os resultados menos precisos.

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

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

private decimal minimumValueDecimal;
Daniel Barbalace
fonte