February 7, 2012

Delegate Syntax in C# for Beginners

Introduction

I have been programming with C# since it came out but I still find the delegate syntax confusing. This is at least partially because Microsoft have changed the recommended syntax regularly over the years. This article is a quick recap of the various syntaxes. It also looks at some of the issues with using them in practice. It’s worth knowing about all the various syntaxes as you will almost certainly see all of them used.

This article is just a recap: it assumes that you know what a delegate is and why you’d want to use one.

.Net and Visual Studio Versions

The first thing to note is that you can use any of these syntaxes as long as you are using Visual Studio 2008 or later and targeting .Net 2.0 or later.

Named methods were available in .Net 1.0, anonymous methods were introduced in .Net 2.0, and lambda expressions were introduced in .Net 3.0. However, like much of .Net 3.0, which is based on the .Net 2.0 assemblies, lambda expressions will compile to .Net 2.0 assuming you have the appropriate version of Visual Studio.

Note also that lambda expressions can do (almost) everything anonymous methods can do, and effectively supersede them as the preferred way of writing inline delegate code.

Code

The Delegate

For all of these examples we need a delegate definition. We’ll use the one below initially.

privatedelegatevoidTestDel(strings);

Named Methods

Named methods are perhaps the easiest delegate syntax to understand intuitively. A delegate is a typesafe method pointer. So we define a method:

privatevoidTest(strings)
{
Console.WriteLine(s);
}

Now we create an instance of our method pointer (the delegate above) and point it at our method. Then we can call our method by invoking the delegate. The code below prints out ‘Hello World 1′. This is easy enough, but all a little cumbersome.

TestDeltd = newTestDel(Test);
td("Hello World 1");

There’s one slight simplification we can use. Instead of having to explicitly instantiate our delegate with the new keyword we can simply point the delegate directly at the method, as shown below. This syntax does exactly the same thing as the syntax above, only (maybe) it’s slightly clearer.

Now when we invoke td3 (in the last line) the code between the curly braces executes.

One advantage of this syntax is that we can capture a local variable in the calling method without explicitly passing it into our new method. We can form a closure. Since in this example we don’t need to pass our string in as a parameter we use a different delegate:

privatedelegatevoidTestDelNoParams();

We can use this as below. Note that the message variable is not explicitly passed into our new method, but can nevertheless be used.

Return Values

Nearly all of the examples above can be extended in a simple way to return a value. The exception is expression lambda which cannot return a value. Doing this is usually an obvious change: we change our delegate signature so that the method it points to returns a value, and then we simply change the method definition to return a value as usual. For example the statement lambda example above becomes as below. The invocation of tdr6 now returns “Hello ” + message2, which we write to the console after the invocation returns:

Events

All of these syntaxes can be used to set up a method to be called when an event fires. To add a delegate instance to an event we used the ‘+=’ syntax of course. Suppose we define an event of type TestDel:

privateeventTestDelTestDelEventHandler;

We can add a delegate instance to this event using any of the syntaxes in an obvious way. For example, to use a statement lambda the syntax is below. This looks a little odd, but certainly makes it easier to set up and understand event handling code.

Passing Delegates into Methods as Parameters: When You Actually Need a Type of ‘Delegate’

The Delegate class is the base class for all delegates, so we can pass any delegate into CallDelegate. However, because the base Delegate class doesn’t know the method signature of the delegate we can’t call Invoke with the correct parameters on the Delegate instance. Instead we call DynamicInvoke with an object[] array of parameters as shown.

Note that there are some methods that take Delegate as a parameter in the framework (e.g. BeginInvoke on a WPF Dispatcher object).

There’s a slightly unobvious change to the ‘Basic Case’ syntax above if we want to call this method using the anonymous method or lambda expression syntax. The code below for calling CallDelegate with an expression lambda does NOT work.

CallDelegate(s => Console.WriteLine(s)); // Expression lambda

The reason is that the compiler needs to create a delegate of an appropriate type, cast it to the base Delegate type, and pass it into the method. However, it has no idea what type of delegate to create.

To fix this we need to tell the compiler what type of delegate to create (TestDel in this example). We can do this with the usual casting syntax (and a few more parentheses) as shown below.

This looks a little strange as we don’t normally need a cast when assigning a derived type to a base type, and in any case we’re apparently casting to a different type to the type the method call needs. However, this syntax is simply to tell the compiler what type of delegate to create in the first place: the cast to the base type is still implicit.

We need to do this for any of the syntaxes apart from the very basic named method syntax (where we’re explicitly creating the correct delegate):

There is one further simplification that we can use in the examples in this article. Instead of defining our own delegates (TestDel etc.) we can use the more generic Action and Func delegates provided in the framework. So, for example, everywhere we use TestDel, which takes a string and returns void, we could use Action<string> instead, since it has the same signature.