Czy mogę zainicjować atrybut C # z tablicą lub inną zmienną liczbą argumentów?

105

Czy jest możliwe utworzenie atrybutu, który można zainicjować ze zmienną liczbą argumentów?

Na przykład:

[MyCustomAttribute(new int[3,4,5])]  // this doesn't work
public MyClass ...
Crono
źródło
12
Po prostu masz niewłaściwą składnię dla tablicy. Powinien to być „nowy int [] {3,4,5}”.
David Wengier

Odpowiedzi:

179

Atrybuty przyjmą tablicę. Chociaż jeśli kontrolujesz atrybut, możesz również użyć paramszamiast niego (co jest przyjemniejsze dla konsumentów, IMO):

class MyCustomAttribute : Attribute {
    public int[] Values { get; set; }

    public MyCustomAttribute(params int[] values) {
       this.Values = values;
    }
}

[MyCustomAttribute(3, 4, 5)]
class MyClass { }

Twoja składnia tworzenia tablic po prostu jest wyłączona:

class MyCustomAttribute : Attribute {
    public int[] Values { get; set; }

    public MyCustomAttribute(int[] values) {
        this.Values = values;
    }
}

[MyCustomAttribute(new int[] { 3, 4, 5 })]
class MyClass { }
Mark Brackett
źródło
33

Możesz to zrobić, ale nie jest to zgodne z CLS:

[assembly: CLSCompliant(true)]

class Foo : Attribute
{
    public Foo(string[] vals) { }
}
[Foo(new string[] {"abc","def"})]
static void Bar() {}

Przedstawia:

Warning 1   Arrays as attribute arguments is not CLS-compliant

W przypadku regularnego używania odbić może być preferowane posiadanie wielu atrybutów, np

[Foo("abc"), Foo("def")]

Jednak to nie zadziała z TypeDescriptor/ PropertyDescriptor, gdzie obsługiwane jest tylko jedno wystąpienie dowolnego atrybutu (pierwsze lub ostatnie wygrywa, nie pamiętam, który).

Marc Gravell
źródło
3
Uwaga: wiele atrybutów wymaga atrybutu AttributeUsage w Twoim atrybucie. stackoverflow.com/questions/553540/…
russau
23

Spróbuj zadeklarować konstruktora w następujący sposób:

public class MyCustomAttribute : Attribute
{
    public MyCustomAttribute(params int[] t)
    {
    }
}

Następnie możesz go używać tak:

[MyCustomAttribute(3, 4, 5)]

Scott Dorman
źródło
12

To powinno być w porządku. Ze specyfikacji, sekcja 17.2:

Wyrażenie E jest wyrażeniem atrybut-argument, jeśli wszystkie poniższe stwierdzenia są prawdziwe:

  • Typ E jest typem parametru atrybutu (§17.1.3).
  • W czasie kompilacji wartość E można rozłożyć na jedną z następujących:
    • Stała wartość.
    • Obiekt System.Type.
    • Jednowymiarowa tablica wyrażeń atrybut-argument .

Oto przykład:

using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class SampleAttribute : Attribute
{
    public SampleAttribute(int[] foo)
    {
    }
}

[Sample(new int[]{1, 3, 5})]
class Test
{
}
Jon Skeet
źródło
5
Uważaj jednak na zgodność z CLS
Marc Gravell
5

Tak, ale musisz zainicjować przekazywaną tablicę. Oto przykład z testu wiersza w naszych testach jednostkowych, który testuje zmienną liczbę opcji wiersza poleceń;

[Row( new[] { "-l", "/port:13102", "-lfsw" } )]
public void MyTest( string[] args ) { //... }
Rob Prouse
źródło
2

Możesz to zrobić. Innym przykładem może być:

class MyAttribute: Attribute
{
    public MyAttribute(params object[] args)
    {
    }
}

[MyAttribute("hello", 2, 3.14f)]
class Program
{
    static void Main(string[] args)
    {
    }
}
Alan
źródło
1

Wracając do odpowiedzi Marca Gravella, tak, możesz zdefiniować atrybut z parametrami tablicy, ale zastosowanie atrybutu z parametrem tablicy nie jest zgodne z CLS. Jednak samo zdefiniowanie atrybutu za pomocą właściwości tablicy jest całkowicie zgodne z CLS.

Uświadomiłem sobie, że Json.NET, biblioteka zgodna z CLS, ma klasę atrybutów JsonPropertyAttribute z właściwością o nazwie ItemConverterParameters, która jest tablicą obiektów.

TBrink
źródło