Blog Archive

Wednesday, August 2

Generics and Delegates

I see a surprising number of search hits on this blog for things involving generics, delegates, and collections. So I guess it would be appropriate to write a series of posts on the subject.

This is all pretty rudimentary stuff, so if you're familiar with generics, delegates, and anonymous methods, you may want to just skip this one.

Generics

public static class Assert
{
    public void Throws<T>(/* ... */) where T: Exception {/* ... */}
}

Here we have a method, named as if it were a part of some test framework (say, NUnit). This method takes one type parameter (T), so we can call this method a "generic method".

This method also contains what is called a "type constraint". That bit beginning with where says that any type that stands in for T must derive from (or be) System.Exception. There are other types of… well… type constraints that I don't think I'll cover in this post.

Okay, so what would we need to know to see if a block of code threw a specific exception? Obviously the type of exception we would expect—that's T. But we would also need the block of code, wouldn't we?

Delegates / Anonymous methods

Delegates have been in C# since forever. Event handling in Forms (and Web Forms) uses delegates extensively. So what's the big fuss?

Well, C# 2.0 introduces the very useful concept of anonymous methods. Arbitrary blocks of code that can be passed around and executed at whim.

Wheras before one had to define a method corresponding to a specific delegate's signature, and then construct the delegate (using syntax that still feels a little odd) before being able to pass it around, you can now say:

public delegate void VoidNoArg();

// ...

VoidNoArg myCode = delegate { Console.WriteLine("Hello world!"); };

Neat. But how useful can a void, no argument delegate really be? Very, actually, through the magic of closures. That is, you can actually say:

string greeting = "Hello world!";

VoidNoArg myCode = delegate { Console.WriteLine("Hello world!"); };
VoidNoArg myCode = delegate { Console.WriteLine(greeting); };

myCode();

// output: Hello world!

Note that the anonymous method referrs to a variable outside of its own scope.

Putting it together

public static class Assert
{
    public void Throws<T>(VoidNoArg code) where T: Exception {/* ... */}
}

// ...

int i = Int32.MaxValue;
Assert.Throws<OverflowException>(delegate { checked(i + 1); });

That's just the tip of the iceberg.

Delegates can be generic. Classes can be generic. Generic classes can have methods that take generic delegates. The System.Collections.Generic collections do all of these things. We'll look at this more in a future post…