C# Tutorial - Method Attributes And Reflection [Intermediate]


Today we are going to take a look at a deep and yet often underused part of C# - method attributes. First we are going to go through what they are and take a look at the built in attributes, and look at how to poke at them through reflection (which we have taken a quick look at before, in Some Notes On Invoking). By the end of the tutorial, through, we will be making our own custom attributes - and we will be using those attributes to make our code more dynamic and extensible.

So what are method attributes? Well, first off, I should probably just call them attributes, since they can be used on a lot more than just methods (although that is all we will be using them for today). You've probably actually already seen them - in fact, I can virtually guarantee that every single one of the Windows Forms C# programs you have written have at least one method with an attribute. Go take a look at the Program.cs that gets generated with every new WinForms C# project. Whats that right above the Main method?
[STAThread]
static void Main()
{
 
Why, that's an attribute! Pretty much, whenever you see some text like that between two square brackets, it's an attribute. We actually used some other attributes here at SOTC just the other day in the XMLSerialization tutorial:
public class Movie
{
  [XmlElement("MovieName")]
  public string Title
  { get; set; }

  [XmlElement("MovieRating")]
  public float Rating
  { get; set; }

  [XmlElement("MovieReleaseDate")]
  public DateTime ReleaseDate
  { get; set; }
}
 
As you can see, The Reddest was applying the XmlElement attribute to those three fields. And, you might notice here, there is more going on than in the [STAThread] example above - the attributes here seem to be taking arguments. This is because what is really happening here is an instance of the XmlElementAttribute class is being created - so a constructor on the XmlElementAttribute class is being run.

So this is all well and good - we have these attributes, and Microsoft has all sorts of nifty ways to use them - serialization, threading, security, etc. But how do we use the attribute concept to do stuff for us? Well, it is actually really easy.

First, you can make custom attributes. Doing so is as simple as making a new class and having it extend Attribute:
class MyToken : Attribute
{
  public string DisplayName { get; set; }

  public MyToken(string dn)
  { DisplayName = dn; }
}
 
So here I've made an extremely simple attribute that just stores a string. Using this attribute is just as easy as making it:
public static class Operations
{
  [MyToken("Operation 1")]
  public static void Operation1()
  {
    System.Windows.Forms.MessageBox.Show("Hey, I'm operation 1!");
  }

  [MyToken("Operation 2")]
  public static void Operation2()
  {
    System.Windows.Forms.MessageBox.Show("Hey, I'm operation 2!");     
  }
}
 
Here I have a class full of static "Operation" methods (that could potentially do a lot more than just pop a message box with a string :P ). I decided to give them each an attribute - in particular, the new MyToken attribute.

So now we have a class with methods tagged with out new attribute, and you are probably wondering "Whats the point?" Well, this is where the cool stuff starts happening - you can access all of these attributes using reflection. Below you can see a screenshot of a little app that tests all this out. The combo box contains a list of all the operation methods in the Operation class, displayed using the DisplayName from the MyToken attribute. When you pick an operation from the combo box, and click execute, the method corresponding to that selection gets executed.

Attribute Test App Screenshot

So let's start walking though the code to do this. First, how do we populate the combo box?
private void PopulateComboBox()
{    
  MethodInfo[] methods = typeof(Operations).GetMethods();

  MyToken token = null;
  List<KeyValuePair<String, MethodInfo>> items = 
      new List<KeyValuePair<string,MethodInfo>>();

  foreach (MethodInfo method in methods)
  {
    token = Attribute.GetCustomAttribute(method, 
        typeof(MyToken), false) as MyToken;
    if (token == null)
      continue;

    items.Add(new KeyValuePair<String, MethodInfo>(
        token.DisplayName, method));

  }

  opComboBox.DataSource = items;
  opComboBox.DisplayMember = "Key";
  opComboBox.ValueMember = "Value";
}
 
So first, we get the collection of methods off of the Operations class. Then we loop through these methods, looking for methods that have the MyToken attribute. As we find them, we add the display name and the method as a key/value pair to a list. Once we have all the operations, we set the DataSource of the combo box to the list, and set the DisplayMember to be the key of the key/value pairs, and the ValueMember to be the value of the key/value pairs.

Now, when the user presses the execute button, the following code gets run:
private void execute_Click(object sender, EventArgs e)
{
  if (opComboBox.SelectedIndex < 0)
    return;

  MethodInfo method = opComboBox.SelectedValue as MethodInfo;

  if (method == null)
    return;

  method.Invoke(null, null);
}
 
If there is an item selected, we pull out the SelectedValue (which will be a MethodInfo). And all we do is invoke it!

The really cool thing about all of this is that if you want to add a new operation, there is only one thing you need to do - add a new method to the Operation class (and give it a MyToken attribute). All the rest of the code is dynamic!

Well, that's it for this tutorial on method attributes. You can download the Visual Studio project for the sample app above here.

Source Files:

Comments