¿Por qué "decimal" no es un tipo de parámetro de atributo válido?

139

Es realmente increíble pero real. Este código no 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; }  
}

Mientras esto 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; }  
}

¿Quién puede decirme por qué el doble está bien mientras que el decimal no lo está?

Cheng Chen
fuente

Respuestas:

139

Esta es una restricción de CLR. Solo se pueden usar constantes primitivas o matrices de primitivas como parámetros de atributo. La razón es que un atributo debe estar codificado completamente en metadatos. Esto es diferente a un cuerpo de método que está codificado en IL. El uso de MetaData solo restringe severamente el alcance de los valores que se pueden usar. En la versión actual de CLR, los valores de metadatos se limitan a primitivas, nulos, tipos y matrices de primitivas (puede haber fallado una menor).

Tomado de esta respuesta por JaredPar .

Los decimales, mientras que un tipo básico no son primitivos y, por lo tanto, no pueden representarse en metadatos, lo que impide que sea un parámetro de atributo.

djdd87
fuente
35
¿Por qué los decimales no se consideran tipos primitivos en el CLR?
koumides
10
@koumides, creo que la respuesta es que el tipo es demasiado grande para expresarse en un solo registro de CPU, ya que es de 128 bits
Chris Marisic
2
Bien, ¿por qué se permiten cadenas como propiedades de atributo? Supongo que pertenece a la categoría de "matriz de primitivas", pero está asignada en montón (tipo de referencia) ...
Steztric
Porque las cadenas son tipos de referencia que se manejan completamente diferentes.
Carsten Schütte
2
@Soren esto no es cierto, Enumson compatibles. Actualmente tengo 2 atributos personalizados, uno con 2 enumeraciones y los otros con una matriz de enumeración.
Franck
60

De las especificaciones :

Los tipos de parámetros posicionales y con nombre para una clase de atributo están limitados a los tipos de parámetros de atributo, que son:

  • Uno de los siguientes tipos: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • El tipo object.
  • El tipo System.Type.
  • Un tipo de enumeración, siempre que tenga accesibilidad pública y los tipos en los que está anidado (si corresponde) también tienen accesibilidad pública (especificación de atributo).
  • Matrices unidimensionales de los tipos anteriores.
Kobi
fuente
10
Correcto, pero tenga en cuenta que está citando una versión anterior de la especificación. En C # versiones 3.0, 4.0, y 5.0, se indica que también puede tener tipo sbyte, ushort, uint, ulong. Y eso parece funcionar bien. Pero todavía decimalno está permitido :-(
Jeppe Stig Nielsen
1
@JeppeStigNielsen He actualizado el enlace de especificaciones y la cita
Ohad Schneider
66
Las primitivas anulables tampoco son compatibles.
KTCO
2

La respuesta a este problema es usar cadenas, que están permitidas como atributos a pesar de no ser de tipo atómico. No use dobles ya que el redondeo hará que los resultados sean menos precisos.

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

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

private decimal minimumValueDecimal;
Daniel Barbalace
fuente