Почему «десятичный» не является допустимым типом параметра атрибута?

139

Это действительно невероятно, но реально. Этот код не будет работать:

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

Пока это работает:

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

Кто может сказать мне, почему double - это нормально, а decimal - нет.

Ченг Чен
источник

Ответы:

139

Это ограничение CLR. В качестве параметров атрибута могут использоваться только примитивные константы или массивы примитивов. Причина в том, что атрибут должен быть закодирован полностью в метаданных. Это отличается от тела метода, которое закодировано в IL. Использование метаданных только строго ограничивает область значений, которые можно использовать. В текущей версии CLR значения метаданных ограничены примитивами, нулем, типами и массивами примитивов (возможно, пропущены второстепенные).

Взято из этого ответа от JaredPar .

Десятичные числа, в то время как базовый тип не является примитивным типом и, следовательно, не могут быть представлены в метаданных, что не позволяет ему быть параметром атрибута.

djdd87
источник
35
Почему десятичные дроби не считаются примитивными типами в CLR?
'40
10
@koumides я считаю, что ответ слишком велик, чтобы его можно было выразить в одном регистре процессора, так как он 128-битный
Крис Марисик,
2
Итак, почему строки разрешены как свойства атрибута? Я предполагаю, что это подпадает под категорию «массив примитивов», но это выделено куча (тип ссылки) ...
Steztric
Потому что строки - это ссылочные типы, которые обрабатываются совершенно по-разному.
Карстен Шютте
2
@ Сорен, это не правда, Enumподдерживаются. В настоящее время у меня есть 2 пользовательских атрибута, один с двумя перечислениями, а другие с массивом перечислений.
Франк
60

Из спецификации :

Типы позиционных и именованных параметров для класса атрибута ограничены типами параметров атрибута:

  • Один из следующих типов: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • Тип object.
  • Тип System.Type.
  • Тип enum при условии, что он имеет общедоступную доступность, а типы, в которые он вложен (если есть), также имеют общедоступную доступность (спецификация атрибута).
  • Одномерные массивы вышеуказанных типов.
Коби
источник
10
Правильно, но учтите, что вы цитируете старую версию спецификации. В C версии 3.0, 4.0, и 5.0 #, утверждается , что она также может иметь тип sbyte, ushort, uint, ulong. И это, кажется, работает хорошо. Но все decimalравно не допускается :-(
Джеппе Стиг Нильсен
1
@JeppeStigNielsen Я обновил ссылку на спецификацию и цитирую
Охад Шнайдер
6
Обнуляемые примитивы также НЕ поддерживаются.
KTCO
2

Ответом на эту проблему является использование строк, которые разрешены в качестве атрибутов, несмотря на то, что они не являются атомарными. Не используйте двойные числа, поскольку округление сделает результаты менее точными.

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

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

private decimal minimumValueDecimal;
Даниэль Барбалас
источник