C# Attributes

Last Updated: 2001

What are they?

Tags that can be applied at class, property, field, method, assembly, module and a few other levels. Not all attributes can be applied at all levels. Attributes typically either signal something (for example, the Obsolete attribute indicates to the compiler that it should generate a message if any other code references the item the attribute is attached to) or to store additional information (for example, the DllImport attribute indicates the location, name and binding mechanism for functions marked as extern). They can however do much more.

Examples

[Obsolete("This message will appear in your build window as a warning if you reference this class elsewhere")]
class myObsoleteClass {}

[Obsolete("This message will appear in your build window as an error if you reference this class elsewhere", true)]
class
myObsoleteMessageBox {
        [DllImport("User32.dll")]
        public
static extern int MessageBox(int h, string m, string c, int type);
}

Namespaces

Attributes are scoped to namespaces like everything else. For example, to use the DllImport attribute the System.Runtime.InteropServices namespace must have been included in the "using" list at the top of the file.

Custom Attributes

Attributes are really classes (derived from System.Attribute). However, these classes do not get constructed until you query the attribute collection for the class (or property or method or ...) you are dealing with. Querying is via reflection and the Attribute static class.

Parameters to an attribute can either be positional ( [myAttribute("testing", 123)] ) or named ( [myAttribute(myString="testing", myInt=123)]. To support positional parameters the Attribute class's constructor must list the parameters in order. to support positional parameters the Attributes class must implement a property per parameters (this is where the names for the parameters come from) and implement a default constructor. There is nothing stopping an Attribute supporting both positional and named parameters at the same time.

The following code shows both the creation of a custom attribute (myAttribute) with both positional and named parameters. The sample code also reads the attribute back, calling a method on that attribute:

using System;

using System.Runtime.InteropServices;

namespace AttributeTest {

    public class myAttribute : System.Attribute {

        // Pubic properties map to named parameters

        // e.g. [myAttribute(myString="testing", myInt=123)]

        public string myString = null;

        public int myInt {

            get {return _myInt;}

            set {_myInt = value;}

        }

        private int _myInt = 0;

        public void myAttributeFunction() {

        Console.Write("\tmyAttributeFunction was called. myString = ");

        Console.WriteLine(myString);

    }

    // Default constructor required if using named parameters

     public myAttribute() {}

    // Specific constructors control positional parameters

     // e.g. [myAttribute("testing", 123)]

     public myAttribute(string strString, int iInt) {

        myString = strString;

        myInt = iInt;

    }

}
 

[myAttribute("hello world", 42)]

public class myClass1 {}
 

[myAttribute(myString="goodbye planet", myInt=999)]

public class myClass2 {}
 

class Start {

    /// The main entry point for the application.

    static void Main(string[] args) {

        // Read attributes through iteration

        OutputDetail(typeof(myClass1));

        OutputDetail(typeof(myClass2));

    }
 

    // Reads & outputs attributes of the class

     static void OutputDetail(Type oType) {

        Console.WriteLine(oType.Name);

        Console.WriteLine("---------");


        // We can get all attributes in one go...

        Console.WriteLine("Multiple Attr check...");

        Attribute[] attr = Attribute.GetCustomAttributes(oType);

        foreach(Attribute a in attr) {

            if (a is myAttribute) {

                ((myAttribute) a).myAttributeFunction();

            }

        }
 

        // Or we can look for a specific one...

        Console.WriteLine("Single Attr check...");

        myAttribute attrSingle = (myAttribute) Attribute.GetCustomAttribute(oType, typeof(myAttribute));

           attrSingle.myAttributeFunction();

           Console.WriteLine();

    }

}

Scoping Custom Attributes
 

To apply an attribute to a class, property, field or method simply include it on the line above (as per examples at the top of the document).

 

To apply an attribute to the module or assembly, use the [module: attribute] and [assembly: attribute] syntax accordingly. For example, [assembly: System.CLSCompliant(true)]

 

To control what items a custom attribute can be applied to, use the AttributeUsage attribute on the Attribute class itself. For example:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class myAttribute : System.Attribute {
    ...

 

Misc
 

The compiler automatically adds the word "Attribute" to the end of attribute names when resolving / building them. As such, you will find the attributes under this longer name in the help file. For example, the AttributeUsage attribute is listed in the MSDN help file as "AtributeUsageAttribute class".