Skip to main content
A generic delegate is a strongly-typed reference to a method that utilizes one or more type parameters in its signature. By deferring the specification of parameter types or return types until instantiation, generic delegates provide type safety and structural reusability without requiring multiple distinct delegate declarations for different data types.

Custom Generic Delegate Declaration

A custom generic delegate is defined using angle brackets (<>) to specify type parameters. These parameters can be applied to the method’s return type, its input parameters, or both.
// T1 and T2 are type parameters for the input parameters; TResult is the type parameter for the return type.
public delegate TResult CustomMapper<T1, T2, TResult>(T1 arg1, T2 arg2);

Instantiation, Assignment, and Invocation

Generic delegates can be instantiated and assigned using lambda expressions, anonymous methods, or method groups. Once assigned, they are invoked using standard method invocation syntax.
// Assignment via lambda expression
CustomMapper<int, int, string> mapper = (x, y) => (x + y).ToString();

// Assignment via method group
Func<int, string> intToString = Convert.ToString;

// Invocation
string result1 = mapper(5, 10);   // Evaluates to "15"
string result2 = intToString(42); // Evaluates to "42"

Built-in Generic Delegates

The .NET Base Class Library (BCL) provides three primary families of generic delegates, which eliminate the need to declare custom generic delegates in most scenarios:
  1. Action<T> Encapsulates a method that takes up to 16 input parameters and returns void. The delegate itself accepts up to 16 generic type parameters corresponding to the types of those input parameters.
    public delegate void Action<in T>(T obj);
    public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
    
  2. Func<T, TResult> Encapsulates a method that takes up to 16 input parameters and returns a value. The delegate supports up to 17 generic type parameters total: up to 16 for the input parameters, plus 1 for the return type (TResult). The return type is always the final type parameter in the signature.
    public delegate TResult Func<out TResult>();
    public delegate TResult Func<in T, out TResult>(T arg);
    
  3. Predicate<T> Encapsulates a method that defines a set of criteria and determines whether the specified object meets those criteria. It accepts exactly one generic type parameter for its single input parameter and always returns a bool.
    public delegate bool Predicate<in T>(T obj);
    

Variance in Generic Delegates

Generic delegates support covariance and contravariance for reference types, declared using the out and in contextual keywords.
  • Covariance (out): Applied to return types. It permits a delegate to return a more derived type than the one specified by the generic type parameter.
  • Contravariance (in): Applied to input parameters. It permits a delegate to accept parameters of a less derived (more generic) type than the one specified by the generic type parameter.
// Covariant delegate (can return a derived type)
public delegate T CovariantDelegate<out T>();

// Contravariant delegate (can accept a base type)
public delegate void ContravariantDelegate<in T>(T item);

Type Constraints

Generic delegates support type constraints using the where clause. This restricts the kinds of types that can be substituted for the type parameters during instantiation, ensuring the delegate only operates on types that fulfill specific contracts (e.g., interfaces, base classes, or reference/value type requirements).
// T must be a reference type and have a parameterless constructor
public delegate void ConstrainedDelegate<T>(T item) where T : class, new();
Tired of Poor C# Skills? Fix That With Deep Grasping!Learn More