Dlaczego „dziesiętny” nie jest prawidłowym typem parametru atrybutu?

139

To naprawdę niewiarygodne, ale prawdziwe. Ten kod nie zadziała:

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

Chociaż to działa:

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

Kto może mi powiedzieć, dlaczego podwójne jest OK, a dziesiętne nie.

Cheng Chen
źródło

Odpowiedzi:

139

To jest ograniczenie CLR. Jako parametry atrybutów można używać tylko stałych pierwotnych lub tablic prymitywów. Powodem jest to, że atrybut musi być zakodowany w całości w metadanych. Różni się to od treści metody, która jest zakodowana w języku IL. Korzystanie z MetaData tylko poważnie ogranicza zakres wartości, których można użyć. W aktualnej wersji środowiska CLR wartości metadanych są ograniczone do prymitywów, wartości null, typów i tablic prymitywów (mogły zostać pominięte mniejsze).

Zaczerpnięte z tej odpowiedzi JaredPar .

Liczby dziesiętne, gdy typ podstawowy nie jest typem pierwotnym i dlatego nie mogą być reprezentowane w metadanych, co uniemożliwia mu bycie parametrem atrybutu.

djdd87
źródło
35
Dlaczego liczby dziesiętne nie są uważane za typy pierwotne w środowisku CLR?
koumides
10
@koumides Myślę, że odpowiedź brzmi: typ jest zbyt duży, aby można go było wyrazić w pojedynczym rejestrze procesora, ponieważ jest to 128
Chris Marisic
2
OK, więc dlaczego ciągi znaków są dozwolone jako właściwości atrybutów? Przypuszczam, że należy on do kategorii „tablica elementów pierwotnych”, ale jest przydzielony do sterty (typ referencyjny) ...
Steztric
Ponieważ ciągi są typami odwołań, które są obsługiwane zupełnie inaczej.
Carsten Schütte
2
@Soren to nieprawda, Enumsą obsługiwane. Obecnie mam 2 atrybuty niestandardowe, jeden z 2 wyliczeniami, a pozostałe z tablicą wyliczeń.
Franck
60

Ze specyfikacji :

Typy parametrów pozycyjnych i nazwanych dla klasy atrybutów są ograniczone do typów parametrów atrybutów, którymi są:

  • Jeden z następujących typów: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  • Typ object.
  • Typ System.Type.
  • Typ wyliczeniowy, pod warunkiem, że ma dostępność publiczną, a typy, w których jest zagnieżdżony (jeśli istnieją), również mają dostępność publiczną (specyfikacja atrybutu).
  • Tablice jednowymiarowe powyższych typów.
Kobi
źródło
10
Dobrze, ale pamiętaj, że cytujesz starą wersję specyfikacji. W języku C # wersji 3.0, 4.0 i 5.0, stwierdza się, że może ona mieć również typ sbyte, ushort, uint, ulong. I wydaje się, że działa dobrze. Ale nadal decimalnie jest dozwolone :-(
Jeppe Stig Nielsen
1
@JeppeStigNielsen Zaktualizowałem link do specyfikacji i cytat
Ohad Schneider
6
Prymitywy dopuszczające wartość null również NIE są obsługiwane.
KTCO
2

Odpowiedzią na ten problem jest użycie łańcuchów, które są dozwolone jako atrybuty, mimo że nie są typem atomowym. Nie używaj podwójnych liczb, ponieważ zaokrąglanie spowoduje, że wyniki będą mniej dokładne.

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

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

private decimal minimumValueDecimal;
Daniel Barbalace
źródło