Programming C#: Attributes and Reflection
Pages: 1, 2, 3, 4, 5, 6, 7, 8, 9
Using an Attribute
Once you have defined an attribute, you can put it to work by placing it immediately before its target. To test the BugFixAttribute of the preceding example, the following program creates a simple class named MyMath and gives it two functions. You'll assign BugFixAttributes to the class to record its code-maintenance history:
[BugFixAttribute(121,"Jesse Liberty","01/03/05")]
[BugFixAttribute(107,"Jesse Liberty","01/04/05",
Comment="Fixed off by one errors")]
public class MyMath
These attributes will be stored with the metadata. Example 18-1 shows the complete program.
Example 18-1: Working with custom attributes
namespace Programming_CSharp
{
using System;
using System.Reflection;
// create custom attribute to be assigned to class members
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class BugFixAttribute : System.Attribute
{
// attribute constructor for
// positional parameters
public BugFixAttribute
(int bugID,
string programmer,
string date)
{
this.bugID = bugID;
this.programmer = programmer;
this.date = date;
}
// accessor
public int BugID
{
get
{
return bugID;
}
}
// property for named parameter
public string Comment
{
get
{
return comment;
}
set
{
comment = value;
}
}
// accessor
public string Date
{
get
{
return date;
}
}
// accessor
public string Programmer
{
get
{
return programmer;
}
}
// private member data
private int bugID;
private string comment;
private string date;
private string programmer;
}
// ********* assign the attributes to the class ********
[BugFixAttribute(121,"Jesse Liberty","01/03/05")]
[BugFixAttribute(107,"Jesse Liberty","01/04/05",
Comment="Fixed off by one errors")]
public class MyMath
{
public double DoFunc1(double param1)
{
return param1 + DoFunc2(param1);
}
public double DoFunc2(double param1)
{
return param1 / 3;
}
}
public class Tester
{
public static void Main( )
{
MyMath mm = new MyMath( );
Console.WriteLine("Calling DoFunc(7). Result: {0}",
mm.DoFunc1(7));
}
}
}
Calling DoFunc(7). Result: 9.3333333333333339
As you can see, the attributes had absolutely no impact on the output. In fact, for the moment, you have only my word that the attributes exist at all. A quick look at the metadata using ILDasm does reveal that the attributes are in place, however, as shown in Figure 18-1. We'll see how to get at this metadata and use it in your program in the next section.
|
Reflection
For the attributes in the metadata to be useful, you need a way to access them -- ideally during runtime. The classes in the Reflection namespace, along with the System.Type and System.TypedReference classes, provide support for examining and interacting with the metadata.
Reflection is generally used for any of four tasks:
- Viewing metadata
- This might be used by tools and utilities that wish to display metadata.
- Performing type discovery
- This allows you to examine the types in an assembly and interact with or instantiate those types. This can be useful in creating custom scripts. For example, you might want to allow your users to interact with your program using a script language, such as JavaScript, or a scripting language you create yourself.
- Late binding to methods and properties
- This allows the programmer to invoke properties and methods on objects dynamically instantiated based on type discovery. This is also known as dynamic invocation.
- Creating types at runtime (Reflection Emit)
- The ultimate use of reflection is to create new types at runtime and then to use those types to perform tasks. You might do this when a custom class, created at runtime, will run significantly faster than more generic code created at compile time. An example is offered later in this chapter.
Viewing MetaData
In this section, you will use the C# Reflection support to read the metadata in the MyMath class.
You start by initializing an object of the type MemberInfo. This object, in the System.Reflection namespace, is provided to discover the attributes of a member and to provide access to the metadata:
System.Reflection.MemberInfo inf = typeof(MyMath);
You call the typeof operator on the MyMath type, which returns an object of type Type, which derives from MemberInfo.
TIP: The
Typeclass is the root of the reflection classes.Typeencapsulates a representation of the type of an object. TheTypeclass is the primary way to access metadata.MemberInfoderives fromTypeand encapsulates information about the members of a class (e.g., methods, properties, fields, events, etc.).
The next step is to call GetCustomAttributes on this MemberInfo object, passing in the type of the attribute you want to find. What you get back is an array of objects, each of type BugFixAttribute:
object[] attributes;
attributes =
inf.GetCustomAttributes(typeof(BugFixAttribute),false);
You can now iterate through this array, printing out the properties of the BugFixAttribute object. Example 18-2 replaces the Tester class from Example 18-1.
Example 18-2: Using Reflection
public static void Main( )
{
MyMath mm = new MyMath( );
Console.WriteLine("Calling DoFunc(7). Result: {0}",
mm.DoFunc1(7));
// get the member information and use it to
// retrieve the custom attributes
System.Reflection.MemberInfo inf = typeof(MyMath);
object[] attributes;
attributes =
inf.GetCustomAttributes(
typeof(BugFixAttribute), false);
// iterate through the attributes, retrieving the
// properties
foreach(Object attribute in attributes)
{
BugFixAttribute bfa = (BugFixAttribute) attribute;
Console.WriteLine("\nBugID: {0}", bfa.BugID);
Console.WriteLine("Programmer: {0}", bfa.Programmer);
Console.WriteLine("Date: {0}", bfa.Date);
Console.WriteLine("Comment: {0}", bfa.Comment);
}
}
Output:
Calling DoFunc(7). Result: 9.3333333333333339 BugID: 121 Programmer: Jesse Liberty Date: 01/03/05 Comment: BugID: 107 Programmer: Jesse Liberty Date: 01/04/05 Comment: Fixed off by one errors
When you put this replacement code into Example 18-1 and run it, you can see the metadata printed as you'd expect.


