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.
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
Post a Comment