Могу ли я инициализировать атрибут C # с помощью массива или другого переменного числа аргументов?

105

Можно ли создать атрибут, который можно инициализировать с переменным количеством аргументов?

Например:

[MyCustomAttribute(new int[3,4,5])]  // this doesn't work
public MyClass ...
Crono
источник
12
Просто у вас неправильный синтаксис массива. Это должно быть «new int [] {3,4,5}».
Дэвид Венгер

Ответы:

179

Атрибуты примут массив. Хотя, если вы контролируете атрибут, вы также можете использовать paramsвместо него (что лучше для потребителей, IMO):

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

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

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

Ваш синтаксис для создания массива просто отключен:

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

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

[MyCustomAttribute(new int[] { 3, 4, 5 })]
class MyClass { }
Марк Брэкетт
источник
33

Вы можете это сделать, но это не соответствует требованиям CLS:

[assembly: CLSCompliant(true)]

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

Показывает:

Warning 1   Arrays as attribute arguments is not CLS-compliant

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

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

Однако это не будет работать с TypeDescriptor/ PropertyDescriptor, где поддерживается только один экземпляр любого атрибута (выигрывает первый или последний, я не могу вспомнить какой).

Марк Гравелл
источник
3
примечание: для нескольких атрибутов требуется атрибут AttributeUsage в вашем атрибуте. stackoverflow.com/questions/553540/…
Руссау
23

Попробуйте объявить конструктор следующим образом:

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

Тогда вы можете использовать это как:

[MyCustomAttribute(3, 4, 5)]

Скотт Дорман
источник
12

Это должно быть нормально. Из спецификации, раздел 17.2:

Выражение E является выражением- атрибут-аргумент, если все следующие утверждения верны:

  • Тип E - это тип параметра атрибута (§17.1.3).
  • Во время компиляции значение E может быть преобразовано в одно из следующих значений:
    • Постоянное значение.
    • Объект System.Type.
    • Одномерный массив выражений-аргументов-атрибутов .

Вот пример:

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
{
}
Джон Скит
источник
5
Однако следите за соблюдением требований CLS
Марк Грейвелл
5

Да, но вам нужно инициализировать массив, который вы передаете. Вот пример теста строки в наших модульных тестах, который проверяет переменное количество параметров командной строки;

[Row( new[] { "-l", "/port:13102", "-lfsw" } )]
public void MyTest( string[] args ) { //... }
Роб Проус
источник
2

Вы можете сделать это. Другой пример:

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

[MyAttribute("hello", 2, 3.14f)]
class Program
{
    static void Main(string[] args)
    {
    }
}
Алан
источник
1

Чтобы воспользоваться ответом Марка Гравелла, да, вы можете определить атрибут с параметрами массива, но применение атрибута с параметром массива несовместимо с CLS. Однако просто определение атрибута с помощью свойства массива полностью соответствует требованиям CLS.

Я понял, что это Json.NET, библиотека, совместимая с CLS, имеет класс атрибутов JsonPropertyAttribute со свойством с именем ItemConverterParameters, которое представляет собой массив объектов.

TBrink
источник