28.1 An attribute class


Using a custom attribute involves writing an attribute class first. Let's examine an example of a custom attribute class called AuthorAttribute : [1]

[1] You will have noticed that I have deliberately made my class fields public. This is, of course, lousy programming practice. The reason is that my reflection codes need to access these fields in a later example. What I should have done, in accordance with good programming practice, is to make these fields private and 'expose' them using public properties. But, for now, I do not wish to complicate issues, especially for those who have not yet read Chapter 20. I will have to bring in properties eventually, in section 28.4.1, but for now, just bear with my public fields.

 1: // AttributeClass.cs  2: using System;  3:  4:  [AttributeUsage(AttributeTargets.Method)]  5: public class AuthorAttribute:Attribute{  6:  7:   public string AuthorName;  8:   public string LastEditDate;  9: 10:   // constructor 11:   public AuthorAttribute(string authorName, String                              lastEditDate){ 12:     this.AuthorName = authorName; 13:     this.LastEditDate = lastEditDate; 14:   } 15: } 

The first thing to realize is that an attribute class must be a direct or indirect subclass of System.Attribute (line 5). Interestingly, you might notice that the class itself is tagged with an [AttributeUsage] attribute specification. [AttributeUsage] is a special standard attribute recognized by the C# compiler. It is used to provide more information about the attribute class you are writing.

[AttributeUsage(AttributeTargets. Method )] on line 4 means that this AuthorAttribute attribute can only be used on methods . Tagging a class, event, delegate, or any other class entity with [AuthorAttribute] will cause a compilation error. If you want your attribute to be used for any class entity, replace line 4 with:

 [AttributeUsage(AttributeTargets.  All  )] 

Lines 11 “ 14 define the constructor for AuthorAttribute . The constructor takes in two strings “ the author's name , and the last edited date (as a string). This is the way the attribute is to be used in MyClass.cs (section 27.1):

 [AuthorAttribute("Name", "23 Dec 02")] 

What the attribute specification above actually does is create an instance of the AuthorAttribute class and pass in the strings "Name" and "23 Dec 02" into the constructor, which in turn initializes fields AuthorName and LastEditDate to these values passed in. You can write other overloaded constructors for your attribute class, as long as the attribute specification matches any one of them.

Since custom attributes are only useful if you want to retrieve their values during runtime via C#'s reflection API, here is another class which does just that.

 1: // Test.cs  2: using System;  3: using System.Reflection;  4:  5: class TestClass{  6:   public static void Main(){  7:  8:     Type type = typeof(MyClass);  9:     object []methods = type.GetMethods(); 10: 11:     foreach(MethodInfo method in methods){ 12:       object []attributes =                              method.GetCustomAttributes(true); 13: 14:       Attribute attr; 15: 16:       for (int i=0; i<attributes.Length; i++){ 17:         attr = (Attribute)attributes[i]; 18:         if (attr is AuthorAttribute){ 19:           AuthorAttribute author = (AuthorAttribute)attr; 20:           Console.Write("method name: " + method.Name); 21:           Console.WriteLine 22:                 (", Written by: " + author.AuthorName); 23:         } 24:       } 25: 26:     } // end foreach 27:   } // end Main 28: } 

You should be able to work out what is happening in Main() since the reflection method names are quite intuitive. See Chapter 16 for more information on reflection.

When TestClass executes, line 8 will assign the Type of the MyClass class to local variable type . Line 9 uses reflection to obtain the methods (encapsulated as an array of MethodInfo object) of the MyClass type dynamically.

For each MethodInfo object, the custom attributes applied to the method are obtained (again through reflection) (on line 12) into an array of Attribute objects. Each method may be associated with more than one attribute. If the method's attribute matches AuthorAttribute , a cast is performed (line 19), and the method's name together with the corresponding Author attribute's AuthorName field are printed out (lines 20 “ 22).

We need to compile all three classes ( Test.cs , AttributeClass.cs , and MyClass.cs in section 27.1). If you are using csc.exe and have coded the classes in three separate source files, this is the way to do it:

 c:\expt>csc AttributeClass.cs MyClass.cs Test.cs 

This command will produce a single Test.exe assembly containing all the codes in the three files. [2]

[2] If you compile multiple C# source files like this, the default assembly name will be the same as the class which contains the Main() method. In this case, since only Test.cs contains a class with a Main() method, the resultant assembly file is Test.exe . In order to change the default assembly name, use the /out option of the compiler. If you compile like this: csc /out:ultimate.exe AttributeClass.cs MyClass.cs Test.cs , you will get ultimate.exe instead of Test.exe . Of the list of source files to compile, only one of them should contain a Main() method. It becomes ambiguous to the compiler if more than one source file contains a class which has a Main() method. In such cases, you have to explicitly tell the compiler which Main() should be the entry point by using the / main option. Assuming that both MyClass.cs and Test.cs have their own Main() methods, you can do this: csc /main:Test AttributeClass.cs MyClass.cs Test.cs to specify that the Main() of the Test class is the entry point when the assembly runs.

Here is the output when Test.exe is executed:

 c:\expt>test method name: DoSomething, Written by: Mok method name: DoSomethingElse, Written by: Mindy method name: DoNothing, Written by: Abigail 

You have dynamically searched for methods tagged with the Author attribute in a .NET assembly, and retrieved the value stored in the public field AuthorName of each Author attribute instance. All this is done during runtime via reflection.



From Java to C#. A Developers Guide
From Java to C#: A Developers Guide
ISBN: 0321136225
EAN: 2147483647
Year: 2003
Pages: 221
Authors: Heng Ngee Mok

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net