C# - Generic Delegates

A generic delegate is a delegate that has type parameters. It allows you to define the delegate type once and use it with different data types, without defining a separate delegate for each data type. This promotes code reusability and type safety.

The .NET Framework has several built-in generic delegate types, such as Func, Action, and Predicate.

1. Func:

  • Represents a delegate that can have a return type and zero or more parameters.
  • The last type parameter is always the return type.

Example:


Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(3, 4));  // Outputs 7

Func<string, string, string> concatenate = (a, b) => a + b;
Console.WriteLine(concatenate("Hello", " World"));  // Outputs "Hello World"

2. Action:

  • Represents a delegate that has no return (void) but can have zero or more parameters.

Example:


Action<string> displayMessage = message => Console.WriteLine(message);
displayMessage("Hello from Action!");  // Outputs "Hello from Action!"

3. Predicate:

  • Represents a delegate that takes one input parameter of type T and returns a bool. Often used for comparisons or conditions.

Example:


Predicate<int> isEven = num => num % 2 == 0;
Console.WriteLine(isEven(4));  // Outputs "True"
Console.WriteLine(isEven(7));  // Outputs "False"

Creating Custom Generic Delegates:

While the built-in generic delegates cater to many common use-cases, you can also define your own generic delegates:


public delegate T MyGenericDelegate<T>(T param1, T param2);

public class Program
{
    public static void Main()
    {
        MyGenericDelegate<int> addIntegers = (a, b) => a + b;
        Console.WriteLine(addIntegers(10, 20));  // Outputs 30

        MyGenericDelegate<string> concatenateStrings = (a, b) => a + b;
        Console.WriteLine(concatenateStrings("Hello", " World!"));  // Outputs "Hello World!"
    }
}

Conclusion:

Generic delegates in C# allow for a more versatile and reusable way to define delegate types. With the ability to parameterize the types they work with, they can reduce redundancy and improve type safety in applications. The built-in generic delegates like Func, Action, and Predicate cater to many standard use-cases and are widely used in .NET for various tasks, including LINQ operations.

Points to Remember:
  1. Type Safety: Generic delegates ensure type safety. Only methods with matching signatures can be assigned based on the provided type argument.
  2. Flexibility: They allow for delegate type definitions without commitment to a specific data type, enabling a single delegate type's use with multiple data types.
  3. Built-in Generic Delegates: C# includes built-in generic delegates like Func, Action, and Predicate. These can be used without defining custom delegates.
    • Func: Denotes a method that returns a value. The last type argument specifies the return type, and the preceding ones are input parameter types.
    • Action: Represents a method returning void with up to 16 input parameters.
    • Predicate: Denotes a method with a single input parameter of type T and returns a boolean.
  4. Type Inference: The C# compiler often infers the correct type arguments when a generic delegate is assigned, enhancing code conciseness.
  5. Varied Signatures: Depending on the number of generic type parameters and constraints, generic delegates can have diverse method signatures.
  6. Constraints: Like other generic entities, constraints can be applied to generic delegate type parameters to limit the types they can work with.
  7. Combination with Other Generic Types: Generic delegates can collaborate with generic classes, interfaces, and methods for high reusability.
  8. Legacy Support: Despite their flexibility, generic delegates are entirely interoperable with non-generic variants.
  9. Lambda Expressions: Generic delegates integrate smoothly with lambda expressions for concise, inline delegate definitions.
  10. Avoiding Repetition: Using generic delegates helps evade defining multiple delegate types that differ only in their parameter or return types.

In essence, generic delegates in C# bolster the power of standard delegates by introducing type parameterization, offering both flexibility and type safety. Being acquainted with them, particularly the built-in Func, Action, and Predicate delegates, is pivotal for modern C# developers.