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 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:
Func(TResult)
Func(T1, TResult)
Func(T1, T2, TResult)
Func(T1, T2, T3, TResult)
Func(T1, T2, T3, T4, TResult)
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.
Comments
Post a Comment