Action and Func delegates in C#

Adil Ansari
3 min readMar 18, 2023

In this article, we are going to discuss Action and Func delegates in C# and how we use them. As we already know, aDelegate in C# is a type-safe method pointer and we are using it to handle the callback or an event. Actions and Funcs are just delegates but are pre-defined in System Namespace so that we don’t need to define the custom delegates manually.

Syntax

Both Action and Func delegates can have multiple input parameters. A Func delegate returns a value while an Action does not, this is the only difference between them.

C# provides 17 overloads of Action delegate in the System Namespace, one of them is:

public delegate void Action<in T>(T obj);

This Action delegate would only accept one argument of type T. However, it can support up to 16 arguments of type T.

Similarly, there are total of 17 overloads of Func delegate defined in the System Namespace, one of them is:

public delegate TResult Func<out TResult>();

This Func delegate doesn't take any input but returns the result of the type TResult. However, it can support up to 16 arguments of type T.

Code Examples

Let’s write a small C# console program so that we understand how to use the Action and Func delegates and what are various ways or expressions to assign a method reference to those.

Let’s start with Action delegate first:

static void PrintAWord(string name) 
{
Console.WriteLine(name);
}

static void Main(string[] args)
{
List<string> words = new List<string> { "apple", "banana", "cherry", "date" };

Action<string> actionPrintWord = PrintAWord;

words.ForEach(w=> actionPrintWord(w));
}

In this example, we are going to print all the string fromList collection so let's create a list of strings first and then an Action<string> delegate that accepts the one string parameter. Now let's use the List<String>.ForEach method with an Action<string> delegate as an argument to print out each string in the list.

We can shorten this example by using the anonymous method:

static void Main(string[] args)
{
List<string> words = new List<string> { "apple", "banana", "cherry", "date" };

words.ForEach(delegate(string word){
Console.WriteLine(word);
});

}

Furthermore, we can make this example more readable by using the lambda expression which we also call the arrow expression:

static void Main(string[] args) 
{
List<string> words = new List<string> { "apple", "banana", "cherry", "date" };

words.ForEach(w=> Console.WriteLine(w));
}

With this, we conclude the various ways to create the Action delegate.

Now let’s see the Func delegate example:

static int Square(int n) 
{
return n * n;
}

static void Main(string[] args)
{
Func<int, int> funcSquare = Square;
Console.WriteLine(funcSquare(5));
}

In this example, we define a Func<int, int> delegate - funcSquare that refers to the method int square(int) which represents a mathematical function that squares a number. Now let's print the square of 5 in the console by calling the funcSquare(5) in the Console.WriteLine() method.

We can shorten this example by using the anonymous method:

static void Main(string[] args) 
{
Func<int, int> funcSquare = delegate(int n) { return n * n; };

Console.WriteLine(funcSquare(5));
}

Furthermore, we can make this example more readable by using the lambda/arrow expression:

static void Main(string[] args) 
{
Func<int, int> funcSquare = n => n * n;

Console.WriteLine(funcSquare(5));
}

With this, we conclude the various ways to create the Func delegate.

Conclusion

In this article, we have learned that Action and Func delegates are easy and quick to define delegates, thus helping us to keep the code shorter and more manageable. We should use generic Action<T> delegate to refer to a method that has zero or more parameters and returns void, and use generic Func<T,TResult> delegate to refer to a method that has zero or more parameters and returns a value.

--

--

Adil Ansari

Experienced C#, .Net, Office 365, and Azure Developer with around 15 years of experience in designing and implementing the maintainable software