Cómo crear atributos permitidos duplicados

96

Estoy usando un atributo personalizado heredado de una clase de atributo. Lo estoy usando así:

[MyCustomAttribute("CONTROL")]
[MyCustomAttribute("ALT")]
[MyCustomAttribute("SHIFT")]
[MyCustomAttribute("D")]
public void setColor()
{

}

Pero se muestra el error "Duplicar atributo 'MyCustomAttribute'".
¿Cómo puedo crear un atributo permitido duplicado?

ebattulga
fuente

Respuestas:

184

Pegue un AttributeUsageatributo en su clase de atributo (sí, eso es un bocado) y configúrelo AllowMultipleen true:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class MyCustomAttribute: Attribute
Anton Gogolev
fuente
6
Solo curiosidad, ¿por qué una clase "sellada"?
Tomas Aschan
18
Microsoft recomienda sellar las clases de atributos siempre que sea posible: msdn.microsoft.com/en-us/library/2ab31zeh.aspx
Anton Gogolev
3
¿Por qué sellado? En resumen: hace que la búsqueda de atributos sea más rápida y no tiene ningún otro impacto.
Noel Widmer
Excepto que evita que cualquier otra persona reutilice su código. Cabe destacar que los atributos de validación en DataAnnotations no están sellados, lo cual es extremadamente útil ya que permite crear especializaciones de ellos.
Neutrino
@Neutrino sellado debe usarse siempre que no espere o no diseñe sus clases para que sean heredadas. Además, cuando la herencia puede convertirse en la fuente de errores, por ejemplo: implementaciones seguras para subprocesos.
Francisco Neto
20

AttributeUsageAttribute ;-p

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyAttribute : Attribute
{}

Sin embargo, tenga en cuenta que si está utilizando ComponentModel ( TypeDescriptor), solo admite una instancia de atributo (por tipo de atributo) por miembro; la reflexión cruda admite cualquier número ...

Marc Gravell
fuente
13

La solución de Anton es correcta, pero hay otro problema .

En resumen, a menos que su atributo personalizado anule TypeId, acceder a él a través de PropertyDescriptor.GetCustomAttributes()solo devolverá una única instancia de su atributo.

Mcdrewski
fuente
Pero funciona a través de: var customAtt = propertyInfo.GetCustomAttributes <MyCustomAttribute> ();
oo_dev
8

De forma predeterminada, Attributelos mensajes de correo electrónico están limitados a aplicarse solo una vez a un solo campo / propiedad / etc. Puede ver esto en la definición de la Attributeclase en MSDN :

[AttributeUsageAttribute(..., AllowMultiple = false)]
public abstract class Attribute : _Attribute

Por lo tanto, como han señalado otros, todas las subclases están limitadas de la misma manera, y si necesita varias instancias del mismo atributo, debe establecer explícitamente AllowMultipleen true:

[AttributeUsage(..., AllowMultiple = true)]
public class MyCustomAttribute : Attribute

En los atributos que permiten múltiples usos, también debe anular la TypeIdpropiedad para asegurarse de que propiedades como PropertyDescriptor.Attributes funcionan como se espera. La forma más sencilla de hacer esto es implementar esa propiedad para devolver la instancia del atributo en sí:

[AttributeUsage(..., AllowMultiple = true)]
public class MyCustomAttribute : Attribute
{
    public override object TypeId
    {
        get
        {
            return this;
        }
    }
}

(Publicar esta respuesta no porque los demás estén equivocados, sino porque es una respuesta más completa / canónica).

Ian Kemp
fuente
3

Como alternativa, piense en rediseñar su atributo para permitir una secuencia.

[MyCustomAttribute(Sequence="CONTROL,ALT,SHIFT,D")]

o

[MyCustomAttribute("CONTROL-ALT-SHIFT-D")]

luego analice los valores para configurar su atributo.

Para obtener un ejemplo de esto, consulte AuthorizeAttribute en el código fuente de ASP.NET MVC en www.codeplex.com/aspnet .

tvanfosson
fuente
3
Incluso es posible hacer que el MyCustomAttributeconstructor tome una matriz de cadenas, a string[], con o sin el paramsmodificador. Entonces podría aplicarse con la sintaxis [MyCustom("CONTROL", "ALT", "SHIFT", "D")](con params).
Jeppe Stig Nielsen
2

Después de agregar AttributeUsage, asegúrese de agregar esta propiedad a su clase Attribute

public override object TypeId
{
  get
  {
    return this;
  }
}
Eje
fuente