Published: 22 Oct 2010
By: Xianzhong Zhu
Download Sample Code

In the previous article, we used the reflection to obtain the information of an assembly, module, type, and type members. In this article, we'll turn to discuss another important aspect related to reflection-Attribute programming. We are going to first look at the mostly-used .NET built-in Attributes, and then try to write a custom Attribute. At last, we'll rest upon reflection to acquire the custom attribute info.

Contents [hide]

The C# 4.0 Reflection Programming series

  • Part 1 An introduction to Reflection in C#.
  • Part 2 As introduced in the first article, the most typically-used tools associated with .NET reflection are: the Type class and Assembly class related members. In this second article, we are going to pick up the .NET reflection tools to set up more samples to explore the wide and extensive use of reflection.
  • Part 3 In the previous article, we used the reflection to obtain the information of an assembly, module, type, and type members. In this article, we'll turn to discuss another important aspect related to reflection-Attribute programming. We are going to first look at the mostly-used .NET built-in Attributes, and then try to write a custom Attribute. At last, we'll rest upon reflection to acquire the custom attribute info.
  • Part 4 In this last article of this series, we will learn what to do with reflection. But before making the topic more interesting, we'll first look at how to dynamically create an object.
  • A "Real" Story

    Many people may not understand the Attribute. So, let's first look at what is Attribute through a practical example. Suppose we have a messaging system. In this system, there exists a method called SendMsg, used to send a short message to someone:

    We soon find having so many parameters listed inside the method would lead to terrible extensibility. Therefore, we'd better define a Message class to encapsulate the short message and then pass a Message object to the method like the following:

    At this point, we should probably replace the old method SendMsg with this better one. Unfortunately, we often can not, because the programs might have been published as a set of API - in many client-side applications the old versioned SendMsg() method had been put into use. So, if we simply remove the old SendMsg() method when updating the system, it may result in the older versioned SendMsg() method unable to work at the client side.

    At this time, what solutions should we fall back upon? We can, of course, achieve this by method overloading, so that we do not need to get rid of the old SendMsg() method. However, if the new SendMsg() method not only has gotten its parameters optimized but also has the algorithm and the efficiency comprehensively optimized, then we will be eager to inform the clients now there is a new high-performance SendMsg() method for use. But if, at this time, the clients have no idea of existing such a new SendMsg () method, what should we do? Certainly, we can call the programmers who maintain the client-side applications or e-mail to them. But none of them is convenient enough. A good way is tell him to recompile the project, so that as long as there exists invocation to the old SendMsg() method the compiler will be informed.

    .Net Built-in Attributes

    In fact, we can rest upon .Net Attributes to accomplish the preceding work. In detail, Attribute is an object that can be loaded into the assembly and objects in the assembly. These objects include the assembly itself, modules, classes, interfaces, structures, constructors, methods, and parameters, etc. The object annotated with attribute(s) is called the target of the attribute(s). In another word, Attribute provides a mechanism to add metadata (data describing data), through which we can provide instruction to the compiler or provide a description of the data.

    Use the System.ObsoleteAttribute attribute

    Now, let's continue the previous story. You will see how to solve the above problem using the ObsoleteAttribute attribute. In detail, we can decorate the old SendMsg() method with the above ObsoleteAttribute attribute to tell the compiler that this method is outdated, and later where there are methods marked with the Obsolete attribute in the program the compiler will give a related warning.

    Now, build the above project, we will find that the compiler gives a warning through the Output window: warning CS0618: 'AttributeProgramming.TestClass.ShowMsg()' is obsolete: 'Please use the new SendMsg(Message msg) overriden method'. By using the Attribute, we can see that the compiler sends out a warning message, telling clients that there is a new method available, so that when programmers see this warning message, they will consider using the new SendMsg() method .

    NOTE:

    For simplicity, the TestClass class and Program are put inside the same assembly. In real cases, they can be very far from each other.

    Use attributes

    Through the above example, we have generally had an idea of how to use Attributes: first, there is a pair of brackets - "[]". And following the left bracket "[" is the name of Attribute, such as Obsolete, after which there is a pair of parentheses - "()." Different from the normal class, this pair of parentheses is not only for the constructor to use to provide parameters, but also can be used to assign values to the properties of the class. As you've seen, in the above case, we've only passed the constructor required parameters.

    NOTE:

    In fact, when you drag the mouse to select the keyword Obsolete and then press F12 to track to the related definition, you will find its full name is ObsoleteAttribute, inherited from the Attribute class. But here only the keyword Obsolete is used to mark methods, which is .Net convention. According to the convention, all the Attributes should be ended with Attribute. When checking objects without the Attribute tag, the compiler will automatically find the version with Attribute.

    What's more, when we use constructors with parameters, the parameters must be provided and the order of them the same as when declaring the constructors. For this, all the parameters related to the constructor are called positional parameters, also named mandatory parameters. Correspondingly, property parameters are also called named parameters, which may be optional.

    Writing custom attributes

    Let's first look at a simple sample as to how to write a custom attribute.

    A simple sample

    If we can not define our own attribute and use it, it will be difficult to understand what attribute is. Let's now roll up our sleeves to build a custom attribute.

    Suppose we have a very common demand: when we create or update a class file, we'd better provide such descriptions of when this class is and who creates it. And what's more, in the future updating we should also indicate when the update takes place and who does it. And also, the updated content may be recorded or not. In the past, how would you do with it? Is it like the following to add comments at the top of the class?

    This indeed can help to achieve the above target, but what if one day we want to save these records to the database for the backup of it? Are we going to go to check out the source files one by one to find those comments and then store them into the database?

    As stressed above, we know that attributes can be used to add metadata to specified types, with which to describe the types. So here, attributes should come in handy. In this case, the metadata should be: annotation type ("update" or "create"), author, date, and notes (optional). The target of the attribute is the DemoClass class.

    Now, we first create a class that encapsulates the above metadata, naming it RecordAttribute:

    NOTE:

    According to .NET requirements, the date in the constructor parameter must be a constant, Type type, or a constant array; we can not directly pass in the DateTime type.

    There is no difference between this class and a normal class. .NET will not identify it an Attribute because of the suffix Attribute. So how could it become an Attribute and applied to a class? Before continuing the story, let's first take a look at how .Net built-in Attribute Obsolete is defined:

    Provide positional parameters and named parameters

    First, we should find the preceding class inherits from the Attribute class. This shows that our custom RecordAttribute should also inherit from the Attribute class.

    Secondly, we find in front of the definition of this Attribute, there are another three attributes to annotate it. These three attributes are: Serializable, AttributeUsage, and ComVisible. Attribute Serializable indicates that a class can be serialized; we have described the AttributeUsage attribute earlier; ComVisible controls accessibility of an individual managed type or member, or of all types within an assembly, to COM. Here we should note that attributes are metadata used to describe the data. In the above case, three attributes are used to decorate the Obsolete attribute. Thus, they can be considered as "meta-metadata".

    Because we need to use the "meta-metadata" to describe our defined attribute RecordAttribute, now we need first understand something about "meta-metadata." Apparently, the "meta-metadata" is also an attribute. In most cases, we only need to use the AttributeUsage attribute. Now, let's look further into it. Let's first look at how the AttributeUsage attribute annotates the above ObsoleteAttribute attribute.

    Then, we continue to look at the definition of the AttributeUsage attribute:

    As is seen, it has only a constructor, which has one positional parameter of the AttributeTargets type, and two named parameters. Note that the ValidOn property is not a named parameter, since it does not contain the set accessor.

    Here, you may wonder: why compartmentalize parameters this way? In reality, this is in relation to the usage of attributes. If AttributeUsageAttribute is a normal class, we must use it like this:

    However, the attribute has to occupy only one line of code, and abut against the target type. How to do with it? Microsoft's software engineers have thought of such an approach: whether the constructor parameters or properties, all should be written in the parentheses of the constructor; however, the constructor parameters must be in accordance with the order and types while for the properties, take the "property=value" format, between which are separated by commas. So, the above code can be reduced like this:

    You can see that AttributeTargets.Class corresponds to the constructor parameter (positional parameter), while AllowMutiple and Inherited are in fact properties (named parameters). Named parameters are optional. We will use the RecordAttribute attribute like this later on. (Why are they called parameters? It may be due to they look more like the parameters of a method.)

    Now, if everything gets ready, then we can use our RecordAttribute attribute like this:

    In this case, recordType, author and date are positional parameters, while Memo is a named parameter.

    The bit flags - AttributeTargets

    As hinted from the name, the AttributeUsage attribute is used to describe the usage of the attributes. Specifically, it is first used is to specify what type or object it identified attributes can be applied to. From the above code, we see the constructor of the AttributeUsage attribute accepts a parameter of the AttributeTargets type. Let's now take a look at AttributeTargets.

    AttributeTargets corresponds to a set of bit flags, which defines the types and objects the related attribute can be applied to.

    Now, you should have understood why in the above example I use this:

    Yet, the AttributeUsage attribute annotated ObsoleteAttribute looks like this:

    Because AttributeUsage corresponds to a set of bit flags, we can use the bitwise OR "|" to combine each bit flag. So, when we write like this:

    It means that this attribute not only can be applied to class but also be applied to interface.

    In addition, do remember there are two exceptions here. Observing the above definition of AttributeUsage, you will notice that attributes can also be used to decorate the Assembly and the Module. However, the two belongs to the compiled results; there not exist such types in the program. How could we decorate them? In fact, you can utilize such syntax:

    It's worth noticing the above statement must be located before the start of the statements in the program.

    The Inherited and AllowMutiple properties

    The AllowMutiple property is used to specify whether the attribute can be repeatedly applied to a type (the default is false), like the following:

    Therefore, we must explicitly set the AllowMutiple attribute to True.

    As for the Inherited property things becomes even more complex. Suppose there is a class inherited from our class DemoClass, then when we have the RecordAttribute attribute added to the class DemoClass, the subclasses of DemoClass will also get the attribute. When the attribute is applied to a method, if a subclass inherited from the class overrides this method, then the Inherited property is used to indicate whether the subclasses' method inherit the attribute.

    In our case, the Inherited is set to false.

    Implement our custom RecordAttribute attribute

    Now, it should be very easy to implement the custom RecordAttribute attribute. For the main body of the class it needs no changes. What we only need to do is to make it inherit from the Attribute base class and use the AttributeUsage attribute to decorate it (assuming we wish to apply this attribute to classes and methods):

    Use the RecordAttribute attribute

    Since we have already created own custom attribute, it is time to use it. The following gives the complete source code.

    Now, press F5 to run the above demo application. You will notice that this program simply outputs on the screen a string "This is a demo class", as shown in Figure 1 below.

    Figure 1: The running-time snapshot related to using custom attribute

    The running-

time snapshot related to using custom attribute

    It seems that our custom attribute like the C# symbol "//" to make comments, has no impact on the program. In fact, however, our attached data have been added as metadata to the assembly. Now, you can take out your .NET ILDasm.exe tool to take a look at what happens behind the scenes, as shown in Figure 2 below.

    Figure 2: Use .NET ILDasm.exe tool to view the custom attribute affecting the class

    Use .NET ILDasm.exe tool to view the custom attribute affecting the class

    Use Reflection to View the Custom Attribute

    Using reflection to view the custom attribute information takes the same way as do other information. First of all, based on the type (in this case DemoClass) to obtain a Type object. Then, call the GetCustomAttributes() method of the Type object to get the attributes applied to this type. When you specify the first parameter of the method GetCustomAttributes(Type attributeType, bool inherit), it will only return the specified type related attribute; otherwise, it will return all the attributes. The second parameter specifies whether to search for the inheritance chain of the member to find these attributes.

    Well, now let's take a look at the output:

    Figure 3: Use reflection to view the custom attribute related info

    Use reflection to 

view the custom attribute related info

    Summary

    Well, up till this point, it will no longer be a problem to put all these data into a database. In conclusion, this article covers one of the important applications of reflection- use it to retrieve all the attributes (whether built-in or custom) related info. In other word, we explored the problem of achieving the late binding to methods using reflection. In the last article, we are going to research into another important usage of reflection- how to dynamically create a type.

    The C# 4.0 Reflection Programming series

  • Part 1 An introduction to Reflection in C#.
  • Part 2 As introduced in the first article, the most typically-used tools associated with .NET reflection are: the Type class and Assembly class related members. In this second article, we are going to pick up the .NET reflection tools to set up more samples to explore the wide and extensive use of reflection.
  • Part 3 In the previous article, we used the reflection to obtain the information of an assembly, module, type, and type members. In this article, we'll turn to discuss another important aspect related to reflection-Attribute programming. We are going to first look at the mostly-used .NET built-in Attributes, and then try to write a custom Attribute. At last, we'll rest upon reflection to acquire the custom attribute info.
  • Part 4 In this last article of this series, we will learn what to do with reflection. But before making the topic more interesting, we'll first look at how to dynamically create an object.
  • <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    About Xianzhong Zhu

    I'm a college teacher and also a freelance developer and writer from WeiFang China, with more than fourteen years of experience in design, and development of various kinds of products and applications on Windows platform. My expertise is in Visual C++/Basic/C#, SQL Server 2000/2005/2008, PHP+MyS...

    This author has published 81 articles on DotNetSlackers. View other articles or the complete profile here.

    Other articles in this category


    C# 4.0 Reflection Programming - Part 1
    An introduction to Reflection in C#.
    C# 4.0 Reflection Programming - Part 2
    As introduced in the first article, the most typically-used tools associated with .NET reflection ar...
    C# 4.0 Reflection Programming - Part 4
    In this last article of this series, we will learn what to do with reflection. But before making the...
    Understanding and Using Extension Methods
    Extension methods were new to C# 3.0. They allow you to add a method to an existing type without hav...
    Introduction to C# 3.0 features
    C# 3.0 introduced some of very useful features built on top of 2.0. This article explains the usage,...

    You might also be interested in the following related blog posts


    Introducing SharePoint 2010 Training at U2U read more
    The Underground at PDC read more
    Building A Product For Real read more
    My History of Visual Studio (Part 6) read more
    F# in VS2010 read more
    My History of Visual Studio (Part 5) read more
    BeginDialOut with Office Communicator Clients read more
    DotNetNuke Fusion Results for Q3 read more
    GiveCamps Get a new Sponsor read more
    Announcing the WebsiteSpark Program read more
    Top
     
     
     

    Discussion


    Subject Author Date
    placeholder Images, please.... Fernando Perez 4/4/2011 1:12 PM
    RE: Images, please.... Sonu Kapoor 4/6/2011 8:51 PM
    placeholder RE: RE: Images, please.... Xianzhong Zhu 4/7/2011 8:10 AM

    Please login to rate or to leave a comment.