DEV Community

Cesar Aguirre
Cesar Aguirre

Posted on • Updated on • Originally published at canro91.github.io

What the Func, Action?

What's the difference between Func and Action? This is a common C# interview question. Let's find it out!

The difference between Func and Action is the return type of the method they point to. Both Func and Action are delegates. They point to a method instead of a built-in or custom type. On one hand, Action references a void method, a method with no return type. And, on the other hand, Func references a method with a return type.

What are delegates?

It all starts with delegates. A delegate is a pointer to a method with some input parameters and possibly a return type. In other words, a delegate is a variable that can hold any method with a given signature. Func and Action are built-in delegate types.

Delegates are helpful when working with higher-order functions. This is, functions that take functions as parameter or return another function. For example, Javascript's callbacks or Python's decorators are high-order functions.

Now, that is clear what delegates are, let's see some Func and Action declarations. For example,

  • Action<Employee> holds a void method that receives Employee as parameter.
  • Action, a void method without any parameters.
  • Func<Employee, string> represents a method that receives an Employee and returns a string.
  • Func<string> doesn't have any parameters and returns string.

What the Func, Action?

Let's get Funcy. Photo by Greyson Joralemon on Unsplash

How to use Func and Action in a method?

You have already used Func, if you have used LINQ. But, in general, you use them as lambda expressions. A lambda expression is an anonymous method. It's a shorthand notation to write a method only with the body and the parameter list.

For example, let's find the employees who have worked for more than ten years.

var allEmployees = new List<Employee> { /* Some employees here */ };

Func<Employee, bool> p = (t) => t.YearsWorked >= 10;
allEmployees.Where(p);
Enter fullscreen mode Exit fullscreen mode

Or just simply

allEmployees.Where(t => t.YearsWorked >= 10);
Enter fullscreen mode Exit fullscreen mode

How to declare a method that receives Func or Action?

To a declare a method that uses Func or Action as an input parameter, you have to use them like regular paramaters. Then, you have to either call Invoke on it or put parenthesis around the name passing the appropiate parameter values.

Let's see an example of a method that uses Func.

public Employee DoSomething(Func<Employee, string> f)
{
    // Create an employee
    var employee = new Employee();

    // string result = f.Invoke(employee);
    // Or simply
    string result = f(employee);

    // Do something with the result here

    return employee;
}
Enter fullscreen mode Exit fullscreen mode

A real-world example

Func and Action are great as small factory methods. They can be used in helper or utility methods to separete business logic from generic code.

Let's see Func in action! Here is an example of Func from Insight.Database to create a ReliableConnection, a database connection that automatically retries on certain errors.

The ExecuteWithRetry method retries things and uses Func for the operation to retry. Some of the code has been removed for brevity.

public class RetryStrategy : IRetryStrategy
{
    public TResult ExecuteWithRetry<TResult>(IDbCommand commandContext, Func<TResult> func)
    {
        int attempt = 0;
        TimeSpan delay = MinBackOff;

        while (true)
        {
            try
            {
                return func();
            }
            catch (Exception ex)
            {
                // if it's not a transient error, then let it go
                if (!IsTransientException(ex))
                    throw;

                // if the number of retries has been exceeded then throw
                if (attempt >= MaxRetryCount)
                    throw;

                // some lines removed for brevity

                // wait before retrying the command
                // unless this is the first attempt or first retry is disabled
                if (attempt > 0 || !FastFirstRetry)
                {
                    Thread.Sleep(delay);

                    // update the increment
                    delay += IncrementalBackOff;
                    if (delay > MaxBackOff)
                        delay = MaxBackOff;
                }

                // increment the attempt
                attempt++;
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

And, this is how to use the method to open a connection.

public class ReliableConnection : DbConnectionWrapper
{
    public override void Open()
    {
        RetryStrategy.ExecuteWithRetry(null, () => { InnerConnection.Open(); return true; });
    }
}
Enter fullscreen mode Exit fullscreen mode

Voilà! That's the difference between Func and Action. Remember that they only represent the signature of a method. You can define or pass around the body later.

Happy Funcy time!

Discussion (0)