Last Updated: February 25, 2016
·
1.457K
· njonsson

Never declare another C♯ delegate again

In case you didn’t know, there’s never a need to declare custom delegates in C♯ code.

By way of a quick review, delegates are C♯’s strongly-typed function pointers. Closures, callbacks, and events are all implemented using delegates. The delegate statement declares the signature of a function pointer and gives that signature a name. You can then assign a variable of that delegate type to a method or an anonymous function whose signature matches the delegate.

An annoying thing about the delegate statement is that it’s hard to know where to put it. Do we nest it inside a class, as shown above? Do we put it in a separate .cs file? Do we group multiple delegate statements in a file, or put each one in its own file?

Another misfortune of custom delegates is that each one is yet another thing that we have to come up with a name for. Naming is often cited as one of the harder things programmers must do. I myself have uttered Hungarian obscenities such as delegate void VoidFormDoubleDelegate(Form form, double @double); when a good name escaped me or when I wanted to reuse the declaration for multiple purposes.

Wouldn’t it be nice to do away with the delegate statement altogether in our applications? We can.

All delegates of a particular arity are alike and can be unified through generics. So all we need is a generic delegate declaration for each possible arity.

Microsoft has provided those declarations in the form of the System.Action and System.Func generic delegates, introduced as long ago as .NET 2.0. These generic delegates accommodate nullary procedures, unary procedures, binary procedures, ternary procedures, and so forth through 10-ary procedures. (‘Nullary’ means zero parameters; it’s represented by either the System.Action delegate or the System.Func<TResult> delegate. A 10-ary procedure can use System.Action<T1 … T10> or System.Func<T1 … T10, TResult>.)

Perhaps you need a delegate for a function that has 11 parameters and a return value. In that case you need to declare your own 11-parameter generic delegate because the .NET Framework Class Library only declares generic delegates up to an arity of 10.

delegate TResult Func<T1,
                      T2,
                      T3,
                      T4,
                      T5,
                      T6,
                      T7,
                      T8,
                      T9,
                      T10,
                      T11,
                      TResult>(T1  arg1,
                               T2  arg2,
                               T3  arg3,
                               T4  arg4,
                               T5  arg5,
                               T6  arg6,
                               T7  arg7,
                               T8  arg8,
                               T9  arg9,
                               T10 arg10,
                               T11 arg11);

If you need to declare an 11-parameter delegate, you’ve got bigger problems to solve than the question of where to put a delegate statement.

(I adapted this protip from a weblog post I wrote in May 2010.)