I know how to use Action
and Func
in .NET, but every single time I start to, the exact same solution can be achieved with a regular old Method that I call instead.
This excludes when an Action
or Func
is used as an argument for something I don't control, like LINQ's .Where
.
So basically my question is...why do these exist? What do they give me extra and new that a simple Method doesn't?
I think other answers here talk about what an Action
/Func
is and its use. I will try to answer how to choose between Action
/Func
and method. The differences first:
1) From a raw performance point of view, delegates are slower compared to direct method calls, but it's so insignificant that worrying about it is a bad practice.
2) Methods can have overloads (same function names with different signatures) but not Action
/Func
delegates since they are declared as variables and by C# rules you cant have two variables with the same name in a given scope.
bool IsIt() { return 1 > 2; }
bool IsIt(int i) { return i > 2; } //legal
Func<bool> IsIt = () => 1 > 2;
Func<int, bool> IsIt = i => i > 2; //illegal, duplicate variable naming
3) Consequently, Action
/Func
are reassignable and can point to any function, while methods once compiled remain to be the same forever. It is semantically wrong to use Func/Action
if the method it points to never changes during run time.
bool IsIt() { return 1 > 2; } //always returns false
Func<bool> IsIt = () => 1 > 2;
IsIt = () => 2 > 1; //output of IsIt depends on the function it points to.
4) You can specify ref
/out
parameters for normal methods. For eg, you can have
bool IsIt(out string p1, ref int p2) { return 1 > 2; } //legal
Func<out string, ref int, bool> IsIt; //illegal
5) You cannot introduce new generic type parameter for Action
/Func
(they are generic already btw, but the type arguments can only be a known type or types specified in the parent method or class), unlike methods.
bool IsIt<A, R>() { return 1 > 2; } //legal
Func<bool> IsIt<A, R> = () => 1 > 2; //illegal
6) Methods can have optional parameters, not Action
/Func
.
bool IsIt(string p1 = "xyz") { return 1 > 2; } //legal
Func<string, bool> IsIt = (p1 = "xyz") => 1 > 2; //illegal
7) You can have params
keyword for parameters of a method, not so with Action
/Func
.
bool IsIt(params string[] p1) { return 1 > 2; } //legal
Func<params string[], bool> IsIt = p1 => 1 > 2; //illegal
8) Intellisense plays well with parameter names of methods (and accordingly you have cool XML documentation available for methods), not so with Action
/Func
. So as far as readability is concerned, regular methods win.
9) Action
/Func
have a parameter limit of 16 (not that you can't define your own ones with more) but methods support more than you will ever need.
As to when to use which, I would consider the following:
When you are forced to use one based on any of the above points, then you anyway have no other choice. Point 3 is the most compelling I find upon which you will have to base your decision.
In most normal cases, a regular method is the way to go. It's the standard way of refactoring a set of common functionality in C# and VB.NET world.
As a rule of thumb, if the function is more than a line, I prefer a method.
If the function has no relevance outside a specific method and the function is too trivial, like a simple selector (Func<S, T>
) or a predicate (Func<bool>
) I would prefer Action
/Func
. For eg,
public static string GetTimeStamp()
{
Func<DateTime, string> f = dt => humanReadable
? dt.ToShortTimeString()
: dt.ToLongTimeString();
return f(DateTime.Now);
}
There could be situations where Action
/Func
makes more sense. For instance if you have to build a heavy expression and compile a delegate, its worth doing it only once and caching the compiled delegate.
public static class Cache<T>
{
public static readonly Func<T> Get = GetImpl();
static Func<T> GetImpl()
{
//some expensive operation here, and return a compiled delegate
}
}
instead of
public static class Cache<T>
{
public static T Get()
{
//build expression, compile delegate and invoke the delegate
}
}
In the first case when you call Get
, GetImpl
is executed only once, where as in the second case, (expensive) Get
will be called every time.
Not to forget anonymous method itself will have certain limits unrelated to Func/Action
, making the use little different. Also see this for a related question.