Skip to main content

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

Popular posts from this blog

C# Snippet - Shuffling a Dictionary [Beginner]

Randomizing something can be a daunting task, especially with all the algorithms out there. However, sometimes you just need to shuffle things up, in a simple, yet effective manner. Today we are going to take a quick look at an easy and simple way to randomize a dictionary, which is most likely something that you may be using in a complex application. The tricky thing about ordering dictionaries is that...well they are not ordered to begin with. Typically they are a chaotic collection of key/value pairs. There is no first element or last element, just elements. This is why it is a little tricky to randomize them. Before we get started, we need to build a quick dictionary. For this tutorial, we will be doing an extremely simple string/int dictionary, but rest assured the steps we take can be used for any kind of dictionary you can come up with, no matter what object types you use. Dictionary < String , int > origin = new Dictionary < string , int >();

C# Snippet - The Many Uses Of The Using Keyword [Beginner]

What is the first thing that pops into your mind when you think of the using keyword for C#? Probably those lines that always appear at the top of C# code files - the lines that import types from other namespaces into your code. But while that is the most common use of the using keyword, it is not the only one. Today we are going to take a look at the different uses of the using keyword and what they are useful for. The Using Directive There are two main categories of use for the using keyword - as a "Using Directive" and as a "Using Statement". The lines at the top of a C# file are directives, but that is not the only place they can go. They can also go inside of a namespace block, but they have to be before any other elements declared in the namespace (i.e., you can't add a using statement after a class declaration). Namespace Importing This is by far the most common use of the keyword - it is rare that you see a C# file that does not h

C# WPF Printing Part 2 - Pagination [Intermediate]

About two weeks ago, we had a tutorial here at SOTC on the basics of printing in WPF . It covered the standard stuff, like popping the print dialog, and what you needed to do to print visuals (both created in XAML and on the fly). But really, that's barely scratching the surface - any decent printing system in pretty much any application needs to be able to do a lot more than that. So today, we are going to take one more baby step forward into the world of printing - we are going to take a look at pagination. The main class that we will need to do pagination is the DocumentPaginator . I mentioned this class very briefly in the previous tutorial, but only in the context of the printing methods on PrintDialog , PrintVisual (which we focused on last time) and PrintDocument (which we will be focusing on today). This PrintDocument function takes a DocumentPaginator to print - and this is why we need to create one. Unfortunately, making a DocumentPaginator is not as easy as