Differences between the Action and Func Delegates in C#

I had always wanted to learn and use the Action and Func delegates in C#. Coincidentally, I wrote on this topic lately, and I am glad to share my learnings here.

In this article, I will discuss the differences between the Action and Func delegate types in C#. I will begin with a definition of delegates in C#, and then proceed to explore the differences between the Action and the Func delegate type.

So, let's jump in.

What Is a Delegate?

A delegate in C# is a pointer to a method. It is a reference type that a method can be assigned to. We can use a delegate to pass a method as a parameter to another method.

Action and Func Delegates

Generic delegates were introduced to make the definition of delegates terser and reduce the need to create custom delegates. The Action and Func<TResult> delegates are two sets of generic delegates that were introduced in .NET 3.5. We use these delegate types to encapsulate a method, without having to explicitly define a custom delegate.

What is the difference between Action and Func Delegates

We use the Action delegate to reference a method whose signature matches that defined by the delegate. Since the Action delegate doesn't return a value, the return type of the method it references must be void.

While the Func<TResult> delegate is used to encapsulate a method whose signature is the same as that defined by the delegate but returns a value.

Action Delegate

To understand how the Action delegate works, let's create a method, Print in the Calculator class that prints a string on the console:

public void Print()
{
    Console.WriteLine("The answer is 200");
}

Next, let's create an Action delegate that references this method:

Action printDelegate = calc.Print;

Finally, we can call the delegate and the method it references in the Main method of the Program class:

Console.Write("Method:");
calc.Print();
Console.Write("Delegate:")
printDelegate();

At this point, we can start the app and view the result on the console:

Method:The answer is 200
Delegate:The answer is 200

As we can see, we can call a method directly, or use a delegate to do the same. Hence, we can call a delegate and pass parameters if required to execute a method the delegate encapsulates.

Using Action<T>

Action<T> is the generic variant of Action. We use it to encapsulate a method that has only one parameter and returns void.

To understand how the Action<T> delegate works, let's define a method PrintResult in the Calculator class. This method takes a single integer parameter and displays it on the Console:

public void PrintResult(int result)
{
    Console.WriteLine($"The result is {result}")
}

We can now define an Action<T> delegate that references the PrintResult method:

Action<int> actionDelegate = calc.PrintResult;

Now, we can use this delegate to execute the method PrintResult. When we call the delegate, we need to pass in the required parameter.

Moreso, we can also use the Action<T> overloads to reference methods that have up to 16 parameters:

Action<Type1, Type2, Type3, ..., Type16> actionDelegate = SomeMethod;

Func<TResult> Delegate

We use the Func<TResult> delegate type to reference a method that matches the defined signature, that is, it has no parameter but has a return value.

To illustrate, let's define a method Result in the Calculator class that returns an integer:

public int Result()
{
    var result = Sum(5, 5);
    return result;
}

Next, we instantiate a Func<TResult> delegate that points to the Result :

Func<int> resultDelegate = calc.Result;

Now, whenever we invoke this delegate, it executes the method Result. As we can see, when we use delegates, we are provided with increased flexibility and functionality.

Using Func<T, TResult>

We use the Func<T, TResult> delegate type and its overloads to encapsulate methods that have 1- 16 parameters, and a return value:

Func<Type1, Type2, Type3, ..., TResult> funcDelegate = SomeMethod2;

It is important to note that the last type defined is that of the return value of the method referenced.

As an illustration, let's define a method Exponent in the Calculator class, that takes in an integer input number, and returns the square of the integer:

public int Exponent(int number)
{
   var result = number * number;

   return result;
}

Next, we create Func<TResult> delegate that encapsulates the method Exponent:

Func<int, int> exponentDelegate = calc.Exponent;

Finally, we can now call this delegate whenever we want to execute the method, and we will get the same result.

Conclusion

We have learned about the Action and Func delegates in this article. We explored the differences between them, and how we can use them in practice.