122

I'm using a custom attribute inherited from an attribute class. I'm using it like this:

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

}

But the "Duplicate 'MyCustomAttribute' attribute" error is shown.
How can I create a duplicate allowed attribute?

Selim Yildiz
  • 5,254
  • 6
  • 18
  • 28
ebattulga
  • 10,774
  • 20
  • 78
  • 116

6 Answers6

229

Stick an AttributeUsage attribute onto your Attribute class (yep, that's mouthful) and set AllowMultiple to true:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class MyCustomAttribute: Attribute
Pang
  • 9,564
  • 146
  • 81
  • 122
Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
  • 8
    Just curious - why a "sealed" class? – Tomas Aschan Feb 16 '09 at 15:09
  • 22
    Microsoft recommends sealing attribute classes whenever possible: http://msdn.microsoft.com/en-us/library/2ab31zeh.aspx – Anton Gogolev Feb 16 '09 at 15:11
  • 3
    Why sealed? In short: Makes attribute lookup faster and has no other impact. – Noel Widmer May 28 '17 at 10:04
  • Except that it stops anyone else from reusing your code. Worth noting that validation attributes in DataAnnotations are _not_ sealed, which is extremely useful as it make it possible to create specializations of them. – Neutrino Feb 05 '19 at 15:22
  • @Neutrino sealed should be used whenever you don't expect or dint't design your classes to be inherited. Plus, when the inheritance may become the source of bugs ex: Thread-safe implementations. – Francisco Neto May 12 '20 at 16:08
20

AttributeUsageAttribute ;-p

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

Note, however, that if you are using ComponentModel (TypeDescriptor), it only supports one attribute instance (per attribute type) per member; raw reflection supports any number...

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
14

Anton's solution is correct, but there is another gotcha.

In short, unless your custom attribute overrides TypeId, then accessing it through PropertyDescriptor.GetCustomAttributes() will only return a single instance of your attribute.

Pang
  • 9,564
  • 146
  • 81
  • 122
mcdrewski
  • 688
  • 7
  • 15
13

By default, Attributes are limited to being applied only once to a single field/property/etc. You can see this from the definition of the Attribute class on MSDN:

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

Therefore, as others have noted, all subclasses are limited in the same way, and should you require multiple instances of the same attribute, you need to explicitly set AllowMultiple to true:

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

On attributes that allow multiple usages, you should also override the TypeId property to ensure that properties such as PropertyDescriptor.Attributes work as expected. The easiest way to do this is to implement that property to return the attribute instance itself:

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

(Posting this answer not because the others are wrong, but because this is a more comprehensive/canonical answer.)

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
4

After you add the AttributeUsage, make sure you add this property to your Attribute class

public override object TypeId
{
  get
  {
    return this;
  }
}
Axle
  • 465
  • 4
  • 5
3

As an alternative, think about redesigning your attribute to allow for a sequence.

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

or

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

then parse the values to configure your attribute.

For an example of this check out the AuthorizeAttribute in ASP.NET MVC source code at www.codeplex.com/aspnet.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 3
    It is even possible to have the `MyCustomAttribute` constructor take an *array* of strings, a `string[]`, with or without the `params` modifier. Then it could be applied with the syntax `[MyCustom("CONTROL", "ALT", "SHIFT", "D")]` (with `params`). – Jeppe Stig Nielsen Oct 13 '13 at 15:15