Action/Func vs Methods, what's the point?

James P. Wright picture James P. Wright · Oct 3, 2011 · Viewed 17.3k times · Source

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?

Answer

nawfal picture nawfal · Jun 29, 2013

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:

  1. 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.

  2. 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.

  3. As a rule of thumb, if the function is more than a line, I prefer a method.

  4. 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);
    }
    
  5. 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.