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 can be more specific if you want to and use functions such as GetMethod to return a single MethodInfo class that is specified by name and other information. For example:

System.Reflection.MethodInfo
             method = typeof(MyClass).
                        GetMethod("MyMethod");
ASCII[] attrs =(ASCII[]) method.
    GetCustomAttributes(typeof(ASCII), false);

There is also a GetMethods function which will return an array of methods that match a name that includes wildcard characters. You can return fields, properties, interfaces and so on and use the corresponding GetCustomAttributes to return an array of attributes applied to them.

Retrieving attributes is generally straightforward – use reflection to get the entity and then call GetCustomAttributes.

One slightly more complicated situation is when an attribute is applied to a parameter. In this case we have to first retrieve the method, then the parameters and then the attributes. To see this in action change the ASCII attribute to read:

[AttributeUsage(AttributeTargets.Method |
                   AttributeTargets.Parameter)]
public  class ASCII : Attribute
{
 public string MyData;
 public string MoreData;
 public ASCII(string MyDataIn)
 {
  MyData = MyDataIn;
 }

Notice that this is an example of OR-ing together parameters used in the constructor call.

The attribute can now be applied to methods and parameters:

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

To retrieve the attribute we first need to get the method:

System.Reflection.MethodInfo method =
         typeof(MyClass).GetMethod("MyMethod");

Then we need to get the parameters of the method:

System.Reflection.ParameterInfo[]
                  pars= method.GetParameters();

Finally we can get and access the parameter attribute:

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

As well as controlling what an attribute can be applied to, you can also control how many times it can be applied using the AttributeUsage attribute and whether or not it is inherited.

For example if you change the ASCII class definition to:

[AttributeUsage(AttributeTargets.Method |
         AttributeTargets.Parameter,
         AllowMultiple=true,Inherited=true )]
public class ASCII : Attribute

you can place multiple attributes on a single entity and the attribute is inherited. For example, following:

[ASCII("Hello Method")]
[ASCII("Hello some more")]
public void MyMethod(string MyParameter)
{
}

GetCustomAttributes will return an array with two instances of ASCII with MyData set to the corresponding string.

Similarly, if you create a new class that inherits from MyClass the attributes are also inherited. For example:

public class MyClass3 : MyClass
{
}

has two ASCII attributes exactly the same as MyClass.

You can, of course forbid multiple attributes and stop attribute inheritance by setting the AllowMultiple and Inherited to false.

 

Banner

Using Attributes

It is very difficult to find a convincing practical example that uses attributes that is general enough to convey the principles and ideas.

If you look at the way attributes are used in C# and in the Framework you will soon discover that they have a limited application without extending the way that they behave to affect the compiler. Of course using custom attributes you can’t do this.

Some built-in attributes do give you a good idea of what they are good at without extra tricks.

For example, the whole p/invoke and com interop marshaling of parameters is a perfect example. You apply marshaling attribute tags to the parameters of external methods you want to call and when you use them the Framework/CLR uses these attributes to decide how to transform managed data into the correct unmanaged data and vice versa.

What makes this a good use of attributes is that the marshaling has to be applied at the class level and once applied isn’t going to be changed at the instance level, and the code that processes the instances can keep all of the mechanisms that load and respond to the attributes applied hidden from view.

As far as the user of the attributes is concerned it’s a matter of applying the attribute, then ignoring it in all the code they write but when they use some supplied machinery the attribute makes it work correctly.

 



Last Updated ( Friday, 19 March 2021 )