C# Func Delegate: Input and Output Types

The Func delegate in C# is a versatile, built-in generic delegate designed to reference methods that take zero or more parameters and return a value. This article explains what the Func delegate is, how it functions in modern .NET development, and the exact rules for declaring its input and output types using generic type arguments.

What is the Func Delegate?

The Func delegate is part of the System namespace and serves as a pre-defined delegate type. Instead of requiring you to declare custom delegates using the delegate keyword, .NET provides Func to handle method references that produce a result. If you need a method that performs an operation and returns a value—such as calculating a sum, formatting a string, or evaluating a boolean condition—you use Func. If your method returns void, C# provides the Action delegate instead.

Specifying Input and Output Types

To specify the input and output types of a Func delegate, you pass type arguments inside angle brackets (< >). The Func delegate can take anywhere from 0 to 16 input parameters, but it must always have exactly one return type.

The fundamental rule for the generic type parameters is: the last type argument is always the return (output) type. Any type arguments listed before the final one correspond to the input parameters in their exact order.

Here is the general breakdown:

  • Zero input parameters: Func<TResult>
    Takes no inputs and returns a value of type TResult.
  • One input parameter: Func<T, TResult>
    Takes one input of type T and returns a value of type TResult.
  • Two input parameters: Func<T1, T2, TResult>
    Takes a first parameter of type T1, a second parameter of type T2, and returns a value of type TResult.
  • Multiple input parameters (up to 16): Func<T1, T2, ..., T16, TResult>
    Takes up to 16 parameters in sequence, with TResult remaining the final output type.

Practical Examples

1. No Input Parameters, Single Output

// Takes no parameters, returns a string
Func<string> getMessage = () => "Hello, World!";

string message = getMessage(); // Returns "Hello, World!"

2. One Input Parameter, Single Output

// Takes an integer, returns an integer
Func<int, int> square = x => x * x;

int result = square(5); // Returns 25

3. Multiple Input Parameters, Different Output Type

// Takes two integers, returns a boolean
Func<int, int, bool> isGreater = (a, b) => a > b;

bool check = isGreater(10, 5); // Returns true

4. Mixed Input Types

// Takes a string and an int, returns a string
Func<string, int, string> repeatText = (text, count) => string.Concat(Enumerable.Repeat(text, count));

string repeated = repeatText("A", 3); // Returns "AAA"

By adhering to the rule that the final type parameter represents the return value, you can easily define signatures for LINQ queries, asynchronous callbacks, and higher-order functions throughout C#.