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 found 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);
write(divide(2,3));
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();
write(divide2(2,3));
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
Anonymous
January 28, 2009
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.Anonymous
January 29, 2009
The comment has been removedAnonymous
January 29, 2009
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?Anonymous
January 29, 2009
The comment has been removedAnonymous
January 29, 2009
Continuing in our weekly blog post series that highlights a few of the new additions to the FrameworkAnonymous
February 10, 2009
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 orAnonymous
March 26, 2009
That's really interesting. And I think it is a good advice for beginners.