C# Tutorial - The Built-In Generic Delegate Declarations

Skill

C# Tutorial - The Built-In Generic Delegate Declarations

Posted in:

Lambda and anonymous functions and delegates are all extremely useful and awesome features of C#, but sometimes using them in any way but the simplest fashion can feel like hard work. This is often because C# is a strictly typed language, and so the compiler always needs to be able to resolve the types for your delegates - to determine where they can and can't be used.

So sometimes, when you find that you need to pass around a delegate, you end up needing to write a delegate declaration. And really, even though it is generally a single simple line of code, it can be very annoying. First, because you have to remember the syntax for writing a delegate declaration, and second because you have to find someplace useful to put it (which is doubly annoying - because you were probably using the anonymous function in the first place because you didn't want have to pull it out and put it somewhere specific).

For instance, take a look a tutorial from about a year ago, the tutorial on how to use the lambda operator. In that tutorial, we wrote a method that would take a delegate and fold it across the rest of the arguments to the function. It looked like this:

public delegate int FoldIntDelegate(int a, int b);

public int Fold(FoldIntDelegate fid, params int[] list)
{
  int result = 1;
  foreach (int i in list)
    result = fid(result, i);
  return result;
}

That first line is the line that I'm complaining about - the line I'd like to get rid of. Well, guess what? You can! There are a whole bunch of basic delegate declaration in the System namespace that cover almost all of the common delegate declaration cases by using generics. For instance, that Fold method could have been written like this:

public int Fold(Func<int, int, int> fid, params int[] list)
{
  int result = 1;
  foreach (int i in list)
    result = fid(result, i);
  return result;
}

So instead of using the custom FoldIntDelegate from the first block of code, here we are using a generic Func delegate (one that allows you to specify two argument types and a return value type. The declaration for this delegate looks like this:

public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);

This delegate declaration will cover any two argument method that has a return value - all you have to do is provide the types for the arguments/return values.

But that's not all - there is a lot more where that came from.

The Funcs

The Funcs are the delegate declaration for delegates that return a value, and were first introduced in .NET 3.5. We have already seen one, the two argument Func. But there are Func declarations for everything from no arguments to 4 arguments:

And here are what their declarations look like:

public delegate TResult Func<TResult>();

public delegate TResult Func<T1, TResult>(T1 arg1);

public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);

public delegate TResult Func<T1, T2, T3, TResult>(T1 arg1, T2 arg2, T3 arg3);

public delegate TResult Func<T1, T2, T3, T4, TResult>(T1 arg1,
    T2 arg2, T3 arg3, T4 arg4);

So if you need more than 4 arguments, you have to make your own declaration. But most of the time, you don't, and so this covers many of the common signatures.

I can hear you now, though - what if I don't want to return a value? Well, don't worry, that is here too:

The Actions

The Actions are the delegate declarations to use when your delegate doesn't return a value. Again, there are Actions for everything from zero (commonly called a thunk) to 4 arguments. And just as a note, the zero argument Action is not actually a generic delegate - cause there is no need for it to be (there are no types to deal with). The single argument Action was introduced in .NET 2, and the rest of them were added in .NET 3.5.

And, for your viewing pleasure, their declarations:

public delegate void Action();

public delegate void Action<T1>(T1 arg1);

public delegate void Action<T1, T2>(T1 arg1, T2 arg2);

public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);

public delegate void Action<T1, T2, T3, T4>(T1 arg1,
    T2 arg2, T3 arg3, T4 arg4);

So between those 10 declarations - the Funcs and the Actions - that about covers pretty much every type of delegate you would ever want to pass around. But there are a couple others that you should probably know about.

Specialized Declarations

All four of these specialized delegates have been around since .NET 2, and actually each of them could be replaced by a Func or an Action. But they do serve useful and specific purposes, and generally I think it is a good idea to use one of these (as opposed to the more generic Func/Action) when you are actually doing what these declarations are meant for.

public delegate int Comparison<T>(T x, T y);

public delegate TOutput Converter<TInput, TOutput>(TInput input);

public delegate bool Predicate<T>(T obj);

public delegate void EventHandler<TEventArgs>(Object sender,  
    TEventArgs e) where TEventArgs : EventArgs;

First off here we have the Comparison declaration (which could also be seen as a two argument Func). Generally a good one to use if you are writing a delegate that compares two items of the same type. A couple places in the framework expect delegates of type Comparison, most notably the Sort function on List.

Next, we have Converter (which is very similar to a two argument Func). This is used when, as the name might imply, the delegate is converting from the input type to the output type. The place where I use this all the time is also on List, the ConvertAll method.

Following Converter, we have Predicate. This can be looked at as an even more specialized Converter - one that always converts to a boolean. Delegates of type Predicate are expected all over the place, but again, I generally use them when dealing with Lists, like the TrueForAll method. Surprisingly, though, many of the LINQ extension methods don't expect predicates, but instead expect Func<T, bool>.

And then we get to our last special case, the EventHandler declaration. Now this here is a handy beast, for whenever you want an event that doesn't use the standard EventArgs. This is because instead of writing code like this:

public class CustomEventArgs : EventArgs
{
  /*My CustomEventArgs Class Content */
}

public delegate void CustomEventHandler(object sender, CustomEventArgs e);

public event CustomEventHandler SomeEvent;

You can write it like this:

public class CustomEventArgs : EventArgs
{
  /*My CustomEventArgs Class Content */
}

public event EventHandler<CustomEventArgs> SomeEvent;

You might say "bah! It is just a two argument action!." You would be right, except that the EventHandler delegate declaration helps enforce a couple useful things - first, that the event has a sender, and second, that whatever event args class is being passed is derived from EventArgs (that is the where clause at the end of the declaration).

That is it for the built-in delegate declarations! Those signatures should really cover almost every delegate that you would ever want to pass around, and hopefully lowers even further the barrier between you and common use of anonymous functions. If you have any questions about how or when to use any of these, leave them below and I will answer them if I can.

Sanket Vasa
12/14/2008 - 10:55

Really useful feature which I am sure was unknown to most programmers…before this post that is.

reply

chester89
06/10/2009 - 15:40

Thanks, great post, I`ve found it very useful

reply

DaharMaster
12/06/2009 - 08:30

Pure gold dust!

Must confess, as a 'part-time' (and unpaid..) developer I've found it hard to keep track of the changes to the language, so while this article might be a bit basic to most it has been a great help to me...:)

Many thanks

reply

luisOn
01/06/2010 - 14:44

Nice, this solution is pretty cool explained, I'm sure a lot of programmers including me, will find this topic very useful.

Thank you!

reply

Big Bob Igor
01/21/2010 - 04:19

Great post man. Cleared up a lot for me.

reply

Booka
01/31/2010 - 05:50

nice article. I googled for guilt-in-delegates since I know they exist. But in my project VS tells me "The Type or namespace "Func" could not be found." Do I have to add an assembly reference? Guess no, cause it is in the System assembly. So what is going wrong here? Why can't I use the built-in's?
Thanks in advance.

reply

Booka
01/31/2010 - 05:51

add: Yes, I am using .NET 3.5 ;)

reply

Booka
01/31/2010 - 06:01

Yes, i missed an assembly: it is included in the System.Core No need for help anymore. Thx.

reply

The Reddest
02/01/2010 - 10:11

Thanks for finding that. It looks like we missed that in the tutorial.

reply

Add Comment

Put code snippets inside language tags:
[language] [/language]

Examples:
[javascript] [/javascript]
[actionscript] [/actionscript]
[csharp] [/csharp]

See here for supported languages.

Javascript must be enabled to submit anonymous comments - or you can login.

Sponsors