Delegation Part 3

26 July 2008 | Scott Williams | .NET, .NET 3.5, C#, Delegates, lambdas

Delegates have come a long way since they were introduced in .NET 1.0. Back then they were only a way to refer to existing methods as variables. 2.0 allowed methods to be inlined, making them "anonymous." The 3.5 Framework does not introduce any new concepts regarding delegates, but it makes writing them much, much quicker and cleaner.

Let’s reuse the example from the last post. C# 2.0’s syntax for anonymous methods looked like this:

private delegate void ItemAction(ListViewItem item, int index);

private void ActOnListView(ItemAction action) {
    for (int i = 0; i < this.listView1.Items.Count; i++) {
        ListViewItem item = this.listView1.Items[i];
        action(item, i);
    }
}

public void HighliteEven() {
    this.ActOnListView(delegate(ListViewItem item, int index) {
        item.BackColor = index % 2 == 0 ? Color.Red : item.BackColor;
    });
}

The HighliteEven() method can be written with C# 3.0 as:

public void HighliteEven() {
    this.ActOnListView((x, y) => x.BackColor = y % 2 == 0 ? Color.Red : x.BackColor);
}

Whoa, where did that => thing come from? That’s the new shorthand for creating an anonymous method. All it does is compresses delegate(Type object){ into something smaller. The (x, y) parameters do not need Type declarations because they are inferred from the delegate’s signature as a ListViewItem and a Int32 (you’ll even get intellisense on them).

This is what allows you to use all of the slick extension methods added to the standard collection classes:

List<string> stuff = new List<string>();
if (stuff.All(s => s.Length > 10)) {
    Console.WriteLine("all strings have at least 10 characters.");
}
string[] myName = stuff.Where(n => n.StartsWith("Scott"));

Single object parameters do not need parentheses, and delegates with no parameters use empty ones: ().

This type of expression is often called a Lambda. Technically, you can have multiple line lambdas, the convention is to stick with a single one1. Multiple lines of code with this syntax can quickly become messy and hard to read. Stick with the old delegate() { } syntax instead if you need it.

[1. I'm not even going to show or link to the syntax, since you shouldn't use it.]

If you are familiar with Python or Ruby (and others) this style should be familiar to you.

	# a lambda in Python
	sq = lambda x: x * x
	sq(5)
	# returns 25

	# a lambda in Ruby
	sq = lambda {|x| x * x}
	sq.call(5)
	# returns 25

.NET also provides a few prefab helper delegates. Action was provided in the 2.0 framework. It’s signature looks like this:

public delegate void Action(T obj);

It has a few overloads to take up to four parameters. So, our sample code up top can be rewritten as:

private void ActOnListView(Action<ListViewItem, int> action) {
	// snip
}

Removing the ItemAction definition.

For methods that need to return something, .NET 3.5 offers several versions of a delegate called Func. The most basic is Func<TResult>(), which takes no parameters and returns an instance of whatever TResult is set to. Func<T, TResult> takes a single parameter, of type T, and also returns a TResult. Like Action, Func provides overloads for up to four parameters. These can all be backported to .NET 2.0, since they don’t use any 3.5-only features:

public delegate TResult Func<TResult>();
public delegate TResult Func<T, TResult>(T obj);
// and so on

The various forms of Func are used throughout the LINQ enabled extension methods of the 3.5 Framework.

The single biggest thing to be aware of is that overuse of delegates makes code difficult to read, especially for the neophyte. If you have a particularly hairy situation, say one that has three or more nested generics and a few lambdas, all on one line, consider splitting it apart in order to increase readability. The compiler won’t care, but maintainers will appreciate it.

If you have never encountered them before, delegates can feel a little awkward at first, but once you get used to them, especially with C# 3.0’s lambda syntax, you won’t want to go back.

Comments are closed.