Click here to Skip to main content
Click here to Skip to main content

Attributes in C#

By , 24 Sep 2002
 

Introduction

Attributes are a new kind of declarative information. We can use attributes to define both design-level information (such as help file, URL for documentation) and run-time information (such as associating XML field with class field). We can also create "self-describing" components using attributes. In this tutorial we will see how we can create and attach attributes to various program entities, and how we can retrieve attribute information in a run-time environment.

Definition

As stated in MSDN (ms-help://MS.MSDNQTR.2002APR.1033/csspec/html/vclrfcsharpspec_17_2.htm)

"An attribute is a piece of additional declarative information that is specified for a declaration." 

Using Pre-defined Attributes

There is a small set of pre-defined attributes present in C#. Before learning how to create our own custom attributes, we will first look at how to use those in our code.
using System;
public class AnyClass 
{
    [Obsolete("Don't use Old method, use New method", true)]
    static void Old( ) { }
   
    static void New( ) { }
   
    public static void Main( ) 
    {
        Old( );
    }
}
Take a look at the this example. In this example we use attribute Obsolete, which marks a program entity that should not be used. The first parameter is the string, which explain why the item is obsolete and what to use instead of this. In fact you can write any othere text here. The second parameter tells the compiler to treat the use of the item as an error. Default value is false, which means compiler generates a warning for this.

When we try to compile above program, we will get an error:

AnyClass.Old()' is obsolete: 'Don't use Old method,  use New method'

Developing Custom Attributes

Now we will see how we can develop our own attributes. Here is a small recipe to create our own attributes.

Derive our attribute class from System.Attribute class as stated in C# language specification (A class that derives from the abstract class System.Attribute, whether directly or indirectly, is an attribute class. The declaration of an attribute class defines a new kind of attribute that can be placed on a declaration) and we are done.

using System;
public class HelpAttribute : Attribute
{
}
Believe me or not we have just created a custom attribute. We can decorate our class with it as we did with obsolete attribute.
[Help()]
public class AnyClass
{
}
Note: it is a convention to use the word Attribute as a suffix in attribute class names. However, when we attach the attribute to a program entity, we are free not to include the Attribute suffix. The compiler first searches the attribute in System.Attribute derived classes. If no class is found, the compiler will add the word Attribute to the specified attribute name and search for it.

But this attribute does nothing useful so far. To make it little useful let add something more in it.

using System;
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Descrition_in)
    {
        this.description = Description_in;
    }
    protected String description;
    public String Description 
    {
        get 
        {
            return this.description;
                 
        }            
    }    
}
[Help("this is a do-nothing class")]
public class AnyClass
{
}
In above example we have added a property to our attribute class which we will query at runtime in last section.

Defining or Controlling Usage of Our Attribute

AttributeUsage class is another pre-defined class that will help us in controlling the usage of our custom attributes. That is, we can define attributes of our own attribute class.

It describes how a custom attribute class can be used.

AttributeUsage has three properties which we can set while placing it on our custom attribute. The first property is:

ValidOn

Through this property, we can define the program entities on which our custom attribute can be placed. The set of all possible program entities on which an attribute can be placed is listed in the AttributeTargets enumerator. We can combine several AttributeTargets values using a bitwise OR operation.

AllowMultiple

This property marks whether our custom attribute can be placed more than once on the same program entity.

Inherited

We can control the inheritance rules of our attribute using this property. This property marks whether our attribute will be inherited by the class derived from the class on which we have placed it.

Let's do something practical. We will place AttributeUsage attribute on own Help attribute and control the usage of our attribute with the help of it.

using System;
[AttributeUsage(AttributeTargets.Class), AllowMultiple = false, 
 Inherited = false ]
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Description_in)
    {
        this.description = Description_in;
    }
    protected String description;
    public String Description
    {
        get 
        {
            return this.description;
        }            
    }    
}
First look at AttributeTargets.Class. It states that Help attribute can be placed on a class only. This implies that following code will result in an error:
AnyClass.cs: Attribute 'Help' is not valid on this declaration type. 
It is valid on 'class' declarations only.
Now try to put in on method
[Help("this is a do-nothing class")]
public class AnyClass
{
    [Help("this is a do-nothing method")]    //error
    public void AnyMethod()
    {
    }
} 
We can use AttributeTargets.All to allow Help attribute to be placed on any program entity. Possible values are: 
  • Assembly, 
  • Module, 
  • Class, 
  • Struct, 
  • Enum, 
  • Constructor, 
  • Method, 
  • Property, 
  • Field,
  • Event, 
  • Interface, 
  • Parameter, 
  • Delegate, 
  • All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,
  • ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface )

Now consider AllowMultiple = false. This state that attribute cannot be placed multiple time.

[Help("this is a do-nothing class")]
[Help("it contains a do-nothing method")]
public class AnyClass
{
    [Help("this is a do-nothing method")]        //error
    public void AnyMethod()
    {
    }
}
It generates a compile-time error.
AnyClass.cs: Duplicate 'Help' attribute
Ok now discuss the last property. Inherited, indicates whether the attribute, when placed on a base class, is also inherited by classes that derive from that base class. If Inherited for an attribute class is true, then that attribute is inherited. However if Inherited for an attribute class is false or it is unspecified, then that attribute is not inherited.

Let suppose we have following class relationship.

[Help("BaseClass")] 
public class Base
{
}

public class Derive :  Base
{
}
We have four possible combinations:
  • [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false ] 
  • [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false ] 
  • [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true ] 
  • [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true ] 

First Case

If we Query (we will see later how to Query attributes of a class at run-time) Derive Class for Help attribute, we will not found it as the inherited attribute is set to false.

Second Case

Second case is no different as inherited attribute is set to false in this case also.

Third Case

To explain the third and fourth cases, let's add the same attribute to the derive class also.
[Help("BaseClass")] 
public class Base
{
}
[Help("DeriveClass")] 
public class Derive :  Base
{
}
Now if we Query about Help attribute, we will get the drive class attribute only as inherited is true but multiples are not allowed so the base class Help is overridden by the Derive class Help attribute.

Fourth Case

In fourth case we will get both attributes when we query our Derive class for Help attribute as both inheritance and multiples are allowed in this case.

Note: AttributeUsage attribute is only valid on classes derived from System.Attribute and both AllowMultiple and Inherited are false for this attribute.

Positional vs. Named Parameters

Positional parameters are parameters of the constructor of the attribute. They are mandatory and a value must be passed every time the attribute is placed on any program entity. On the other hand Named parameters are actually optional and are not parameters of the attribute's constructor.

To explain it in more detail, let's add another property in our Help class.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false,
 Inherited = false)]
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Description_in)
    {
        this.description = Description_in;
        this.verion = "No Version is defined for this class";
    }
    protected String description;
    public String Description
    {
        get 
        {
            return this.description;
        }
    }
    protected String version;
    public String Version
    {
        get 
        {
            return this.version;
        }
        //if we ever want our attribute user to set this property, 
        //we must specify set method for it 
        set 
        {
            this.verion = value;
        }
    }
}
[Help("This is Class1")]
public class Class1
{
}

[Help("This is Class2", Version = "1.0")]
public class Class2
{
}

[Help("This is Class3", Version = "2.0", 
 Description = "This is do-nothing class")]
public class Class3
{
}
When we Query Class1 for Help attribute and its properties, we will get:

Help.Description : This is Class1
Help.Version :No Version is defined for this class
As we didn't define any value for Version property, the value set in the constructor is used. If no value is defined then the default value of the type is used (for example: in case int the default value is zero).

Now, Querying Class2 will result

Help.Description : This is Class2
Help.Version :  1.0
Don't use multiple constructors for optional parameter. Instead, mark them as named parameters. We called them named because when we supply their value in the constructor, we have to name them. For example, in second class, we define Help.
[Help("This is Class2", Version = "1.0")]
In the AttributeUsage example, the ValidOn parameter is a Positional parameter and Inherited and AllowMultiple are named parameters.

Note: To set the value of named parameter in the constructor of the attribute, we must supply the set method for that property otherwise it will generate a compile time error:

'Version' : Named attribute argument can't be a read only property
Now what happen when we will query Class3 for Help attribute and its properties? The result is the same compile-time error.
'Desciption' : Named attribute argument can't be a read only property
Now modify the Help class and add the set method of Description. Now the output will be:
Help.Description : This is do-nothing class 
Help.Version : 2.0
What happen behind the scene is first the constructor is called with positional parameters, and then set method is called for each named parameter. The value set in the constructor is override by the set method.

Parameters Types

The types of parameters for an attribute class are limited to:
  • bool, 
  • byte, 
  • char, 
  • double, 
  • float,
  • int, 
  • long, 
  • short, 
  • string 
  • System.Type 
  • object 
  • An enum type, provided that it and any types in which it is nested are publicly accessible. A one-dimensional array involving any of the types listed above 

Attributes Identifiers

Let's suppose, we want to place Help attribute on entire assembly. The First Question arises where to place that Help attribute so that compiler would determine that it is placed on an entire assembly?? Consider another situation; we want to place an attribute on the return type of a method. How compiler would determine that we are placing it on a method-return-type and not on entire method??

To resolve such ambiguities, we use attribute identifier. With the help of attribute identifiers, we can explicitly state the entity on which we are placing the attribute.

For example:

[assembly: Help("this a do-nothing assembly")]
The assembly identifier before the Help attribute explicitly tells the compiler that this attribute is attached to entire assembly. The possible identifiers are 
  • assembly
  • module
  • type
  • method
  • property
  • event
  • field
  • param
  • return

Querying Attributes at Run-Time

We have seen how to create attributes and how to attach them to a program element. Now its time to learn how user of our class can query this information at run-time.

To query a program entity about its attached attributes, we must use reflection. Reflection is the ability to discover type information at run time.

We can use the .NET Framework Reflection APIs to iterate through the metadata for an entire assembly and produce a list of all classes, types, and methods that have been defined for that assembly.

Remember the old Help attribute and the AnyClass class.

using System;
using System.Reflection;
using System.Diagnostics;

//attaching Help attribute to entire assembly
[assembly : Help("This Assembly demonstrates custom attributes 
 creation and their run-time query.")]

//our custom attribute class
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Description_in)
    {
        //
        // TODO: Add constructor logic here
        this.description = Description_in;
        //
    }
    protected String description;
    public String Description
    {
        get 
        {
            return this.deescription;
                 
        }            
    }    
}
//attaching Help attribute to our AnyClass
[HelpString("This is a do-nothing Class.")]
public class AnyClass
{
//attaching Help attribute to our AnyMethod
    [Help("This is a do-nothing Method.")]
    public void AnyMethod()
    {
    }
//attaching Help attribute to our AnyInt Field
    [Help("This is any Integer.")]
    public int AnyInt;
}
class QueryApp
{
    public static void Main()
    {
    }
}
We will add attribute-query code to our Main method in the next two sections.

Querying Assembly Attributes

In the following code, we get the current process name and load the assembly using LoadFrom method of Assembly class. Then we use GetCustomAttributes method to get all the custom attributes attached to the current assembly. Next foreach statement iterate through all the attributes and try to the cast the each attribute as Help attribute (casting objects using the as keyword has an advantage that if the cast is invalid, we don't have to worry about an exception being thrown. What will happen instead is that the result will be null). The next line check if cast is valid and not equal to null then it display the Help property of the attribute.
class QueryApp
{
    public static void Main()
    {
        HelpAttribute HelpAttr;

        //Querying Assembly Attributes
        String assemblyName;
        Process p = Process.GetCurrentProcess();
        assemblyName = p.ProcessName + ".exe";

        Assembly a = Assembly.LoadFrom(assemblyName);

        foreach (Attribute attr in a.GetCustomAttributes(true))
        {
            HelpAttr = attr as HelpAttribute;
            if (null != HelpAttr)
            {
                Console.WriteLine("Description of {0}:\n{1}", 
                                  assemblyName,HelpAttr.Description);
            }
        }
}
}
The output of the following program is:
Description of QueryAttribute.exe:
This Assembly demonstrates custom attributes creation and 
their run-time query.
Press any key to continue

Querying Class, Method and Field Attributes

In the code below, the only unfamiliar is the first line in Main method.
Type type = typeof(AnyClass);
It gets the Type object associated with our AnyClass class using typeof operator. The rest of the code for querying class attributes is similar to the above example and don't need any explanation (I think).

For querying method's and field's attributes, we first get all the methods and fields present in the class then we query their associated attributes in the same manner as we did class attributes

class QueryApp
{
    public static void Main()
    {

        Type type = typeof(AnyClass);
        HelpAttribute HelpAttr;


        //Querying Class Attributes
        foreach (Attribute attr in type.GetCustomAttributes(true))
        {
            HelpAttr = attr as HelpAttribute;
            if (null != HelpAttr)
            {
                Console.WriteLine("Description of AnyClass:\n{0}", 
                                  HelpAttr.Description);
            }
        }
        //Querying Class-Method Attributes  
        foreach(MethodInfo method in type.GetMethods())
        {
            foreach (Attribute attr in method.GetCustomAttributes(true))
            {
                HelpAttr = attr as HelpAttribute;
                if (null != HelpAttr)
                {
                    Console.WriteLine("Description of {0}:\n{1}", 
                                      method.Name, 
                                      HelpAttr.Description);
                }
            }
        }
        //Querying Class-Field (only public) Attributes
        foreach(FieldInfo field in type.GetFields())
        {
            foreach (Attribute attr in field.GetCustomAttributes(true))
            {
                HelpAttr= attr as HelpAttribute;
                if (null != HelpAttr)
                {
                    Console.WriteLine("Description of {0}:\n{1}",
                                      field.Name,HelpAttr.Description);
                }
            }
        }
    }
}
The output of the following program is.
Description of AnyClass:
This is a do-nothing Class.
Description of AnyMethod:
This is a do-nothing Method.
Description of AnyInt:
This is any Integer.
Press any key to continue

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

sadaf alvi
United States United States
Member
BS from University of Karachi.
http://www24.brinkster.com/salvee/

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questiongreat articlememberwadigzon Diaz-wong30 Jan '13 - 8:25 
some few typos here and there, but a great article nonetheless.
GeneralMy vote of 5memberhoangvantu1 Dec '12 - 3:14 
ok
GeneralMy vote of 5memberNirmal kumar 52 Aug '12 - 19:28 
Really vry gud explanation about attributes and reflection from base 2 top.. really it will help for beginners .. Thanks man..
GeneralMy vote of 5memberFrewCen19 May '12 - 9:08 
Good introduction!
GeneralRe: My vote of 5memberhomesea20 May '12 - 20:17 
thanks! but i don't know when i need to use an attribute.
GeneralRe: My vote of 5memberFrewCen21 May '12 - 4:59 
So you're not programming enought. You can use attributes for example in creating custom control - it's needed alot, in creating copyright hidden mark, for setting method execution.......
QuestionCan I create a custom class for EditorBrowsable?memberSalam633112 Jan '12 - 20:03 
Hello sadaf Alvi,
 
Can I create a custom class for EditorBrowsable, inorder to hide the public properties and methods from the same assembly/solution ?
if there anything to achieve this, please let me know.
 

 
Regards
Muhammed abdussalam
GeneralMy vote of 5memberferfr8 Dec '11 - 6:47 
Good, very useful, great examples.
GeneralGreat tutorialmembermsaroka2 Mar '11 - 13:04 
Everything I could ask for, easy to follow.
GeneralMy vote of 3memberAmol_B27 Sep '10 - 20:16 
nice
GeneralMy vote of 3memberjak0b20 Sep '10 - 1:09 
same informations as on msdn
GeneralRe: My vote of 3memberrajeevtechnical17 Oct '11 - 22:29 
Agree.
Quick Homepage - www.PageMoon.com

GeneralExcellent!memberaccent1232 Feb '10 - 8:36 
Ausome!
You get my 5
QuestionHow to load entity properties conditionallymemberMember 159112730 Sep '09 - 1:25 
Hi,
Suppose i have an entity class which belongs to version table in the database having following properties for the fileds(columns)
#region Properties
private string _Program;
 
public string Program
{
get { return _Program; }
set { _Program = value; }
}
 
private string _AllowVersion;
 
public string AllowVersion
{
get { return _AllowVersion; }
set { _AllowVersion = value; }
}
#endregion
 
Suppose in a method i need to pass only one property so i will have to intitalize Entity class of version
which will have two properties but i need only one.
Do we have any way to tell version entity class that only load AllowVersion property only while initializing version entity and discard rest of the properties i.e my VersionEntity object must have only
one field.
 
I guess the only possible ways could be attributes or reflection. If possible please provide code snippet
to achieve this.
Is it possible to do this without performance cost??
 
Thanks In Advance
GeneralVery nice article Sadaf AlvimemberKamran Shahid14 Aug '09 - 5:47 
Very very nice article Sadaf
Generalgoodmemberhoang tru21 Jul '09 - 12:38 
a useful article, now i can program with attribues. thank you verymuch.
GeneralA little typomemberLyesainer12 Jun '09 - 3:17 
There's just a little typo in the code in this article
 

using System;
public class HelpAttribute : Attribute
{
    public HelpAttribute(String Descrition_in)
    {
        this.description = Description_in;
    }
    protected String description;
    public String Description 
    {
        get 
        {
            return this.description;
                 
        }            
    }    
}
[Help("this is a do-nothing class")]
public class AnyClass
{
}
 

 
just a P missing over there in the bolded word.
 
Hunchback. Wheels. Lyesainer. Tyler Durden. V. Don Quixote. Martin Sillenas. You. Me.
GeneralPretty inspiring. Could this be better.memberZijian10 May '09 - 18:38 
rather than looping through items of attributes of an object, could it be more general or better to use things like
 
if (myProperty.Attributes.ContainKey(type(MyAttribute)))
{
MyAttribute myAttribute = myProperty.Attributes[typeof(MyAttribute)] as MyAttribute
// ... Do something on myAttribute
}
 
Provided that myProperty has property SettingsAttributeDictionary Attributes.
 
Zijian

GeneralThanksmemberAbhishek sur3 Oct '08 - 1:45 
I really dont know how attributes are accessed during runtime. Now I know. Really thankful for your article. Voted 5 for you... Rose | [Rose] Rose | [Rose]
 
Abhishek Sur


GeneralGreat Tutorial, BUT.....memberJames_Lin2 Sep '08 - 18:24 
the tutorial doesn't show how the Anyclass can use the attributes...
GeneralGreat artical but I've found one small issue on the code snap you've mentionedmemberJennifer_Shen6 Jul '08 - 17:41 
using System;
[AttributeUsage(AttributeTargets.Class), AllowMultiple = false,
Inherited = false ]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this.description = Description_in;
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
}
 

 
AttributeTargets, AllowMultiple and Inherited are three properties of AttributeUsage. If directly typing above code, Compiler will not allow to pass. Just cut the right blanket and paste it at the end of AttributeUsage attribute. See below code.
 
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false,
Inherited = false)]
public class HelpAttribute : Attribute
{
public HelpAttribute(String Description_in)
{
this.description = Description_in;
}
protected String description;
public String Description
{
get
{
return this.description;
}
}
}
 

Generalnice, here is a similar concept "dynamic properties at runtime". [modified]membercodezilla942 Oct '07 - 13:57 
nice, here is a similar concept http://www.spikesolutions.net/ViewSolution.aspx?ID=080da5ff-9c72-4760-9612-ac5448180a65[^]The solution comes with a user control for defining dynamic properties (DataType, Formatting, Validations) that render on an object at runtime. It also comes with a user control for setting dynamic properties on on an instance of an object based on the definitions. Various Web controls along with validation controls are rendered based on the definitions.
 

-- modified at 20:34 Tuesday 2nd October, 2007
QuestionWrong attribute name?memberDaniel Lees14 Sep '07 - 9:14 
Why have you named your attribute Help, when you've defined your custom attribute as HelpAttribute? Attribute names are class names. Because an attribute is a class, an attribute's name is a classname. I'm confused...please help me understand.
AnswerRe: Wrong attribute name?memberSenthil Kumaran25 Oct '08 - 6:56 
By convention, all attribute names end with the word "Attribute" to distinguish them from other items in the .NET Framework. However, you do not need to specify the attribute suffix when using attributes in code. For example, [Help] is equivalent to [HelpAttribute], but HelpAttribute is the attribute's actual name in the .NET Framework.
 
Senthil (ursenix)
GeneralgoodmemberBackkom26 Apr '07 - 22:31 
Laugh | :laugh: good

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 25 Sep 2002
Article Copyright 2002 by sadaf alvi
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid