Framework Design Guidelines: Avoiding custom delegates

Continuing in our weekly blog post series that highlights a few of the new additions to the Framework Design Guidelines 2nd edition.. This content is foundimage[5]_thumb[2]_thumb[2]_thumb in the Events and Callbacks section of Chapter 6: Designing for Extensibility. I am impressed by how simple new additions to the BCL can have such a large (and positive) effect on framework design. As these new advances come out, learn them and use them!

DO use the new Func<…>, Action<…>, or Expression<…> instead of custom delegates, when defining APIs with callbacks.

Func<…> and Action<…> represent generic delegates. The following is how .NET Framework defines them:

public delegate void Action()
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)
public delegate TResult Func<TResult>()
public delegate TResult Func<T, TResult>(T arg)
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)

They can be used as follows:

Func<int,int,double> divide = (x,y)=>(double)x/(double)y;
Action<double> write = (d)=>Console.WriteLine(d);

Expression<…> represents function definitions that can be compiled and subsequently invoked at runtime but can also be serialized and passed to remote processes. Continuing with our example:

Expression<Func<int,int,double>> expression =(x,y)=>(double)x/(double)y;
Func<int,int,double> divide = expression.Compile();

Notice how the syntax for constructing an Expression<> object is very similar to the one used to construct a Func<> object; in fact, the only difference is the static type declaration of the variable (Expression<> instead of Func<>).


Most times you’re going to want Func or Action if all that needs to happen is to run some code. You need Expression when the code needs to be analyzed, serialized, or optimized before it is run. Expression is for thinking about code, Func/Action is for running it.

Comments (7)

  1. Kevin Westhead says:

    I think this is good advice although it’s worth mentioning that you may not always have access to these delegates. All of the above delegates are defined in .NET 3.5, which rules them out if you’re targeting .NET 2.0. Other potentially suitable delegates can also bring in an unwanted dependency or have a misleading name. E.g. if you need a delegate that does not return a value and accepts no parameters for .NET 2.0 you might consider MethodInvoker, but this brings a dependency on Windows Forms. You could also consider ThreadStart, but this implies the delegate will be invoked on a different managed thread. So while I agree with the advice in general, there are still legitimate scenarios where I think a custom delegate will offer greater clarity for an API.

  2. Lee Campbell says:

    Interesting. I’m generally a bit of a slave to the FDG, but here (and EventArg<T,KV,X,Y,Z>) I draw the line. I dont realy have a problem with a single Type Parameter on the action or a 2 on a Func, but when I have

    Action<int, int, int> resize

    I lose the important parameter information. Is the first parameter the width or height or scale? We used EventArgs<T,K,V> in a CAB application and it ended up being analogous to just passing object[] everywhere. However they became very useful as Base classes for EventArg types which allowed us to provide meaningful names to the type and its parameters. Intention revealing interfaces anyone?

    Love to here your feedback (& Juval Lowy’s feedback)!

  3. Russell says:

    Interesting note and it kind of makes sense, but I wish it were a little less dogmatic ("DO use the new Func, Action or Expression instead of custom delegates") and provided more explanation of why that’s a good thing.

    The delegates look reasonable and, now that I know of them I’ll probably use them, but, as Lee Campbell points out, custom delegates can be made clearer.

    So, what’s lost in not using the new delegates?

  4. Christopher Harris says:

    So I guess Func<> was one of those names that couldn’t be "Function" because of language keywords huh?  FDG FTW.

  5. Continuing in our weekly blog post series that highlights a few of the new additions to the Framework

  6. It’s snowing again. Like every other time in Southern-most NJ (near Philly) if it accumulates more than an inch, I will be astonished. Very unlike where we used to live in New Hampshire, where for 2 years, they have at least weekly snow storms of 8 or

  7. Web Designer says:

    That’s really interesting. And I think it is a good advice for beginners.