How to Create Generic Delegates in C
Generic delegates in C# allow you to define type-safe callback
functions and method references without locking them to a single data
type. By using type parameters, generic delegates provide maximum
reusability and efficiency across different parts of your application.
This guide covers how to define custom generic delegates, apply type
constraints to them, and leverage built-in generic delegates such as
Action and Func.
Defining a Custom Generic Delegate
To create a generic delegate, use the delegate keyword
followed by the return type, the delegate name, and angle brackets
< > containing one or more type parameters.
// Definition of a generic delegate
public delegate TResult Transformer<TInput, TResult>(TInput input);
class Program
{
static void Main()
{
// Instantiate the delegate with specific types (string, int)
Transformer<string, int> stringLengthCalculator = CountCharacters;
int length = stringLengthCalculator("Hello World");
Console.WriteLine($"Character count: {length}"); // Output: 11
// Reuse the same delegate definition with different types (double, double)
Transformer<double, double> squareCalculator = Square;
double result = squareCalculator(5.0);
Console.WriteLine($"Square result: {result}"); // Output: 25
}
static int CountCharacters(string text) => text.Length;
static double Square(double number) => number * number;
}Adding Generic Constraints
You can restrict the types that can be supplied to your generic
delegate by using generic constraints with the where
keyword. This ensures compile-time validation for required base classes,
interfaces, or constructors.
public delegate void EntityHandler<T>(T entity) where T : class, IComparable<T>;
class Program
{
static void ProcessItem(string item)
{
Console.WriteLine($"Processing: {item}");
}
static void Main()
{
// Valid because string is a class and implements IComparable<string>
EntityHandler<string> handler = ProcessItem;
handler("Valid Entity");
}
}Using Built-in Generic Delegates
In modern C#, creating custom delegates is often unnecessary because the .NET framework provides flexible built-in generic delegates:
Action<...>: Used for methods that returnvoidand accept from 0 to 16 parameters.Action<string, int> printDetails = (name, age) => Console.WriteLine($"{name} is {age} years old."); printDetails("Alice", 30);Func<..., TResult>: Used for methods that return a value and accept from 0 to 16 parameters (the last type parameter denotes the return type).Func<int, int, int> add = (a, b) => a + b; int sum = add(10, 20); // sum is 30Predicate<T>: Used specifically for methods that evaluate an input against a condition and return abool.Predicate<int> isEven = x => x % 2 == 0; bool check = isEven(4); // check is true
Custom generic delegates should primarily be declared when you need
custom parameter attributes, specific variance keywords (in
for contravariance, out for covariance), or distinct
descriptive naming beyond what Action and Func
offer.