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# 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...

C# WPF Tutorial - Implementing IScrollInfo [Advanced]

The ScrollViewer in WPF is pretty handy (and quite flexible) - especially when compared to what you had to work with in WinForms ( ScrollableControl ). 98% of the time, I can make the ScrollViewer do what I need it to for the given situation. Those other 2 percent, though, can get kind of hairy. Fortunately, WPF provides the IScrollInfo interface - which is what we will be talking about today. So what is IScrollInfo ? Well, it is a way to take over the logic behind scrolling, while still maintaining the look and feel of the standard ScrollViewer . Now, first off, why in the world would we want to do that? To answer that question, I'm going to take a an example from a tutorial that is over a year old now - Creating a Custom Panel Control . In that tutorial, we created our own custom WPF panel (that animated!). One of the issues with that panel though (and the WPF WrapPanel in general) is that you have to disable the horizontal scrollbar if you put the panel in a ScrollV...