Using Action and Func in C#
If you are working with C# on a daily basis, I am sure that you have ran into delegates, Action, and Func types. If you can understand the difference between these types and when you should use them, your code will end up being very flexible and maintainable at the same time.
What Are Delegates?
I will keep it ultra short. A delegate is a super type-safe function pointer in C#. They will allow you to pass a method as a parameter, store them in a variable, and invoke them later when you need it.
You can think of delegates as a contract that defines how your method should look like. interested in delegates? check out my Events and Delegates section in the Advanced C# Features collection.

Building A Data Processor
Alright, let's build something real. A scenario for a real world project could be a data processor. Data is the new gold, so why not make something that could process the data for us?
I will now show you a data processor that is capable of doing various mathematical operations- and validations on data. We will take off with some custom delegates, and then utilize Action, and Func to make the code simple.
Are you ready? Here are the delegates - one for validation, and one for calculation on the data.
namespace TWC.DelegateExamples;
public delegate bool ValidationDelegate(int value);
public delegate int CalculationDelegate(int a, int b);
That is pretty straight forward. Let's now implement the actual data processor.
namespace TWC.DelegateExamples;
public class DataProcessor
{
// Using the first custom delegate
public void ProcessWithValidation(int value, ValidationDelegate validator)
{
if (validator(value))
{
Console.WriteLine($"✓ Value {value} passed validation");
}
else
{
Console.WriteLine($"✗ Value {value} failed validation");
}
}
// Using Action (for operations that don't return a value)
public void ExecuteOperation(int a, int b, Action<int, int> operation)
{
Console.WriteLine($"Executing operation on {a} and {b}...");
operation(a, b);
}
// Using Func (for operations that return a value)
public void Calculate(int a, int b, Func<int, int, int> calculation)
{
var result = calculation(a, b);
Console.WriteLine($"Calculation result: {result}");
}
// Func with different return type
public void AnalyzeResult(int a, int b, Func<int, int, string> analyzer)
{
var analysis = analyzer(a, b);
Console.WriteLine($"Analysis: {analysis}");
}
}I know it is really simple, but it is only to show you the difference and maybe wake a light inside you for how awesome this is?! 😎
Using the Data Processor
Okay now that we have the data processor in place with some simple logic, let's bring it to life.
public class Program
{
public static void Main()
{
var processor = new DataProcessor();
// 1. Custom Delegate Example
ValidationDelegate isPositive = (value) => value > 0;
processor.ProcessWithValidation(42, isPositive);
processor.ProcessWithValidation(-5, isPositive);
// You can also pass the lambda directly
processor.ProcessWithValidation(100, (value) => value % 2 == 0);
Console.WriteLine();
// 2. Action Example (no return value)
processor.ExecuteOperation(10, 5, (a, b) =>
{
var sum = a + b;
Console.WriteLine($" Sum: {sum}");
});
processor.ExecuteOperation(10, 5, (a, b) =>
{
var product = a * b;
Console.WriteLine($" Product: {product}");
});
Console.WriteLine();
// 3. Func Example (returns a value)
processor.Calculate(15, 3, (a, b) => a / b);
processor.Calculate(8, 7, (a, b) => a * b);
Console.WriteLine();
// 4. Func with different return type
processor.AnalyzeResult(20, 10, (a, b) =>
{
var ratio = (double)a / b;
return ratio > 1.5 ? "Significant difference" : "Similar values";
});
}
}If you a new to custom delegates, Action, or Func. The code above might seem a bit confusing, I totally get that. So... please let me explain the difference.
Key Differences Explained
Custom Delegates
- When to use? When you want to define a reusable way a method should look for your domain.
- Pros? Easier to understand because the name tells you what it’s meant to do.
- Cons? Requires explicit declaration.
Action<T1, T2, ...>
- When to use? When you need to pass a method that performs an operation but doesn't return a value.
- A signature can take 0-16 parameters, returns
void. - An example could be
Action<int, int>that takes two integers and returns nothing.
In C#, a method’s signature is the shape of the method. The word signature might seem quite technical for some, but using it daily will start making it the default go-to 😅 Signature is the specific combination of the following in a method.
- Its name
- Its parameter list (types, number, and order)
- Its return type (For
delegatesandinterfaces, the return type is considered part of the signature. In class methods it doesn’t affect overload resolution, though it still contributes to how we think about the method’s overall structure. Makes sense? 😄)
Func<T1, T2, ..., TResult>
- When to use? When you need to pass a method that calculates or returns a value, like in my example above.
- Again signature can take 0-16 parameters. Remember that the last type parameter is always the return type!
- An example could be
Func<int, int, string>that takes two integers and returns a string.
When To Use What?
I get it... it might seem overwhelming or confusing. To fix that, I have created a diagram that might help you.

Action, or FuncReal-World Use Cases
To make things more simple or easier to undertand, I have broken them down into some real-world use cases.
Custom Delegates should be used for Event handlers, and callback patterns with specific domain meaning.
public delegate void OrderProcessedDelegate(Order order, DateTime timestamp);Action should also be used for Event handlers, but also async callbacks, and LINQ operations like ForEach().
items.ForEach(item => Console.WriteLine(item));Func should be used for LINQ queries, factory patterns, and transformation pipelines.
var doubled = numbers.Select(n => n * 2);
var filtered = items.Where(item => item.IsActive);My Best Practices
- I prefer
ActionandFuncfor most cases. Why? They are baked-in and universally understood. - I use custom delegates when the name adds clarity to my domain (like
ValidationDelegate). - I Keep lambdas simple. If they're complex, extract them to named methods. Will make your life so much easier 🙌
- I always consider async variants. This could be
Func<Task>andFunc<Task<T>>for async operations.
Summary
Custom delegates, Action, and Func all overlap each other in terms of purpose. If you want to write idiomatic C# code, you need to understand their differences. I always use custom delegates when a clear, and domain-drive name will provide me value. Finally I use Action and Func when I would like to write some code that provide a simple, and convenient callback.
What are you using and why? Please let me know about that and any other related questions in the comments. Thank you for reading this far! ✨
