Como criar atributos permitidos duplicados

96

Estou usando um atributo personalizado herdado de uma classe de atributo. Estou usando assim:

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

}

Mas o erro "Duplicar atributo 'MyCustomAttribute'" é mostrado.
Como posso criar um atributo permitido duplicado?

ebattulga
fonte

Respostas:

184

Cole um AttributeUsageatributo em sua classe de Atributo (sim, isso é demais) e defina AllowMultiplecomo true:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class MyCustomAttribute: Attribute
Anton Gogolev
fonte
6
Só por curiosidade - por que uma classe "selada"?
Tomas Aschan
18
A Microsoft recomenda selar classes de atributos sempre que possível: msdn.microsoft.com/en-us/library/2ab31zeh.aspx
Anton Gogolev
3
Por que selado? Resumindo: torna a pesquisa de atributos mais rápida e não tem outro impacto.
Noel Widmer
Exceto que isso impede que qualquer outra pessoa reutilize seu código. Vale ressaltar que os atributos de validação em DataAnnotations não são lacrados, o que é extremamente útil, pois possibilita a criação de especializações dos mesmos.
Neutrino
@Neutrino seal deve ser usado sempre que você não espera ou não projeta que suas classes sejam herdadas. Além disso, quando a herança pode se tornar a fonte de bugs, por exemplo: implementações thread-safe.
Francisco Neto
20

AttributeUsageAttribute ;-p

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

Observe, entretanto, que se você estiver usando ComponentModel ( TypeDescriptor), ele suporta apenas uma instância de atributo (por tipo de atributo) por membro; reflexão crua suporta qualquer número ...

Marc Gravell
fonte
13

A solução de Anton está correta, mas há outra pegadinha .

Resumindo, a menos que seu atributo customizado substitua TypeId, acessá-lo por meio de PropertyDescriptor.GetCustomAttributes()retornará apenas uma única instância de seu atributo.

mcdrewski
fonte
Mas funciona via: var customAtt = propertyInfo.GetCustomAttributes <MyCustomAttribute> ();
oo_dev
8

Por padrão, os Attributes são limitados a serem aplicados apenas uma vez a um único campo / propriedade / etc. Você pode ver isso na definição da Attributeclasse no MSDN :

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

Portanto, como outros observaram, todas as subclasses são limitadas da mesma maneira e, caso você precise de várias instâncias do mesmo atributo, é necessário definir explicitamente AllowMultiplecomo true:

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

Em atributos que permitem vários usos, você também deve substituir a TypeIdpropriedade para garantir que as propriedades PropertyDescriptor.Attributes funcionem conforme o esperado. A maneira mais fácil de fazer isso é implementar essa propriedade para retornar a própria instância do atributo:

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

(Postar esta resposta não porque as outras estejam erradas, mas porque esta é uma resposta mais abrangente / canônica.)

Ian Kemp
fonte
3

Como alternativa, pense em redesenhar seu atributo para permitir uma sequência.

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

ou

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

em seguida, analise os valores para configurar seu atributo.

Para obter um exemplo disso, verifique AuthorizeAttribute no código-fonte ASP.NET MVC em www.codeplex.com/aspnet .

Tvanfosson
fonte
3
É ainda possível fazer com que o MyCustomAttributeconstrutor pegue um array de strings, a string[], com ou sem o paramsmodificador. Em seguida, ele pode ser aplicado com a sintaxe [MyCustom("CONTROL", "ALT", "SHIFT", "D")](com params).
Jeppe Stig Nielsen
2

Depois de adicionar o AttributeUsage, certifique-se de adicionar esta propriedade à sua classe de atributo

public override object TypeId
{
  get
  {
    return this;
  }
}
Eixo
fonte