Deep C# - Custom Attributes In C#
Written by Mike James   
Friday, 19 March 2021
Article Index
Deep C# - Custom Attributes In C#
Attribute Semantics
Practical Attributes
Using Attributes
An Example
Conclusion

You might think that as you can pass an object you can pass anything but this isn’t the case. There is an additional condition that restricts parameters to constants of the appropriate type.

So you can have:

[ASCII("Hello Method",MyObject=1)]

and even:

[ASCII("Hello Method",MyObject="Hello")]  

but you can’t have:

[ASCII("Hello Method", MyObject=new object())]

or anything like it.

Each time you request the details of an attribute a new object is instantiated no matter what class is involved.

So for example, if we go back to the simpler version of ASCII with a single parameter constructor, the following:

[ASCII("Hello Attribute World")]
public class MyClass
{
}

[ASCII("Hello Again")]
public class MyClass2
{
}

does indeed create distinct attribute instances as you can demonstrate:

ASCII[] attrs1=(ASCII[])typeof(MyClass).
           GetCustomAttributes(
                         typeof(ASCII), false);
ASCII[] attrs2=(ASCII[])typeof(MyClass2).
           GetCustomAttributes(
                         typeof(ASCII), false);
MessageBox.Show(attrs1[0].MyData);
MessageBox.Show(attrs2[0].MyData);

The result is two messageboxes that correctly show each of the messages set by the attribute tags.

However you get two instances even if you call GetCustomAttributes a second time on the same class.

What isn’t generally realised is that you can change attribute values fairly easily at runtime. The reason is, of course, that the instances of the attribute classes that are created are perfectly normal objects and can be used without restriction.

For example, we can get the object:

ASCII[] attrs1=(ASCII[])typeof(MyClass).
             GetCustomAttributes(
                       typeof(ASCII), false);

change the value of its public variable and show that it has changed:

attrs1[0].MyData="A New String";
MessageBox.Show(attrs1[0].MyData);

and finally create another instance and show that its value is unchanged:

ASCII[] attrs3=(ASCII[]) typeof(MyClass).
               GetCustomAttributes(
                      typeof(ASCII), false);
MessageBox.Show(attrs3[0].MyData);

Banner

 

Practical Attributes

Now that we have the basic mechanism that is used to implement attributes out in the open and perfectly clear we need to investigate some of the intricacies of using attributes in a real situation.

The first thing to say is that you can apply attributes to more than just a class. To restrict what an attribute can be applied to you need to apply the AttributeUsage attribute to the class that is associated with your custom attribute - yes an attribute applied to your custom attribute!

For example:

[AttributeUsage(AttributeTargets.Method)]
public  class ASCII : Attribute

restricts the ASCII attribute to being applied to methods only. If you try to apply it to anything else the compiler will generate an error message.

IntelliSense provides a list of AttributeTargets and you can OR targets together to produce a set of things that the attribute can be applied to - an example of this is given later.

Being able to tag other entities with attributes raises the question of how to get at them at runtime?

The answer is that there a number of overloaded versions of GetCustomAttributes which return arrays of attributes associated with different types of entity.

For example, GetCustomAttributes(memberinfo) will retrieve an array of attributes applied to the member specified by the memberinfo class.

To see how this works first add a member function to MyClass and tag it with the ASCII attribute:

public class MyClass
{
 [ASCII("Hello Method")]
 public void MyMethod()
 {
 }
}

To retrieve attributes associated with a member we first have to retrieve its memberinfo. There are many ways of doing this but in most cases specifying it by name is sufficient using the GetMember function:

System.Reflection.MemberInfo[] member =
         typeof(MyClass).GetMember("MyMethod");

The GetMember function retrieves all members that match the name specified string which can include wildcard characters.

In this case it should just return a memberinfo class corresponding to the unique MyMethod function. To retrieve any attributes applied to the method we can use the usual:

Attribute[] attrs = Attribute.
             GetCustomAttributes(member[0]);

Or if you just want the ASCII attributes that have been applied we can use the alternative:

ASCII[] attrs =(ASCII[]) member[0].
                     GetCustomAttributes(
                       typeof(ASCII), false);
MessageBox.Show(attrs[0].MyData);

The GetMember function will return members of a range of types – methods, properties etc.

csharp

 



Last Updated ( Friday, 19 March 2021 )