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 receivesEmployee
as parameter. -
Action
, a void method without any parameters. -
Func<Employee, string>
represents a method that receives anEmployee
and returns astring
. -
Func<string>
doesn't have any parameters and returnsstring
.
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);
Or just simply
allEmployees.Where(t => t.YearsWorked >= 10);
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;
}
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++;
}
}
}
}
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; });
}
}
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!