¿Puedo inicializar un atributo de C # con una matriz u otro número variable de argumentos?

105

¿Es posible crear un atributo que se pueda inicializar con un número variable de argumentos?

Por ejemplo:

[MyCustomAttribute(new int[3,4,5])]  // this doesn't work
public MyClass ...
Crono
fuente
12
Simplemente tiene la sintaxis incorrecta para la matriz. Debe ser "new int [] {3,4,5}".
David Wengier

Respuestas:

178

Los atributos tomarán una matriz. Aunque si controla el atributo, también puede usar paramsen su lugar (que es más agradable para los consumidores, en mi opinión):

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

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

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

Su sintaxis para la creación de matrices simplemente está desactivada:

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
fuente
33

Puede hacerlo, pero no cumple con CLS:

[assembly: CLSCompliant(true)]

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

Muestra:

Warning 1   Arrays as attribute arguments is not CLS-compliant

Para el uso de reflexión regular, puede ser preferible tener múltiples atributos, es decir

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

Sin embargo, esto no funcionará con TypeDescriptor/ PropertyDescriptor, donde solo se admite una única instancia de cualquier atributo (ya sea la primera o la última victoria, no recuerdo cuál).

Marc Gravell
fuente
3
nota: varios atributos requieren un atributo AttributeUsage en su atributo. stackoverflow.com/questions/553540/…
russau
23

Intenta declarar el constructor así:

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

Entonces puedes usarlo como:

[MyCustomAttribute(3, 4, 5)]

Scott Dorman
fuente
12

Eso debería estar bien. De la especificación, sección 17.2:

Una expresión E es una expresión -argumento-atributo si todas las siguientes afirmaciones son verdaderas:

  • El tipo de E es un tipo de parámetro de atributo (§17.1.3).
  • En tiempo de compilación, el valor de E se puede resolver en uno de los siguientes:
    • Un valor constante.
    • Un objeto System.Type.
    • Una matriz unidimensional de expresiones-argumento-atributo .

He aquí un ejemplo:

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
fuente
5
Sin embargo
tenga
4

Sí, pero necesita inicializar la matriz que está pasando. Aquí hay un ejemplo de una prueba de fila en nuestras pruebas unitarias que prueba un número variable de opciones de línea de comando;

[Row( new[] { "-l", "/port:13102", "-lfsw" } )]
public void MyTest( string[] args ) { //... }
Rob Prouse
fuente
2

Usted puede hacer eso. Otro ejemplo podría ser:

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

[MyAttribute("hello", 2, 3.14f)]
class Program
{
    static void Main(string[] args)
    {
    }
}
Alan
fuente
1

Para aprovechar la respuesta de Marc Gravell, sí, puede definir un atributo con parámetros de matriz, pero aplicar un atributo con un parámetro de matriz no es compatible con CLS. Sin embargo, simplemente definir un atributo con una propiedad de matriz es perfectamente compatible con CLS.

Lo que me hizo darme cuenta de esto fue que Json.NET, una biblioteca compatible con CLS, tiene una clase de atributo JsonPropertyAttribute con una propiedad llamada ItemConverterParameters que es una matriz de objetos.

TBbeber
fuente