Action, Func, and Predicate delegates in C#

By FoxLearn 12/31/2024 6:38:05 AM   96
Delegates in C# are powerful tools that help manage method callbacks and provide flexibility in code.

A delegate is a type-safe function pointer, allowing methods to be passed as arguments to other methods.

What are Func and Action Delegates?

Func and Action are two of the most commonly used delegates in C#.

  • Action Delegate: This delegate points to methods that accept parameters but do not return a value (i.e., void).
  • Func Delegate: This delegate points to methods that accept parameters and return a value.

For example, Using an Action Delegate:

static void Main(string[] args)
{
    Action<string> action = new Action<string>(DisplayMessage);
    action("Welcome to C#!");
}

static void DisplayMessage(string message)
{
    Console.WriteLine(message);
}

In this example, Action<string> points to the DisplayMessage method, which prints the provided string to the console.

For example, Using a Func Delegate:

static void Main(string[] args)
{
    Func<int, double> func = new Func<int, double>(CalculateTax);
    Console.WriteLine(func(50000));  // Outputs 15000
}

static double CalculateTax(int salary)
{
    return salary * 0.3;
}

Here, Func<int, double> points to the CalculateTax method, which calculates and returns the tax based on the salary provided.

Predicate Delegates in C#

A Predicate delegate is used for methods that take a single parameter and return a bool. Predicate delegates are commonly used to perform search operations by evaluating elements against specific criteria.

For example, Using a Predicate Delegate:

class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}

static void Main(string[] args)
{
    List<Customer> customers = new List<Customer>
    {
        new Customer { Id = 1, Name = "Alice" },
        new Customer { Id = 2, Name = "Bob" }
    };

    Predicate<Customer> findCustomer = c => c.Name == "Alice";
    Customer customer = customers.Find(findCustomer);
    Console.WriteLine(customer.Name);  // Outputs "Alice"
}

In this example, a Predicate<Customer> is used to search a list of customers by name.

A Predicate delegate is essentially a specialized version of Func, where Func can return any type, while Predicate specifically returns a boolean value. Both are used to define methods that take parameters, but Predicate is typically used for filtering or searching based on conditions.

Variance in Delegates

C# allows flexibility in delegates through covariance and contravariance. Covariance allows a delegate to return a more derived type than expected, and contravariance allows the delegate to accept a less derived type as an argument.

class Animal { }
class Dog : Animal { }

public delegate Animal MyDelegate();

public static Animal ReturnAnimal() { return new Animal(); }
public static Dog ReturnDog() { return new Dog(); }

public static void Main()
{
    MyDelegate delegateA = ReturnAnimal;
    MyDelegate delegateB = ReturnDog;  // This works due to covariance
}

Here, MyDelegate can point to both ReturnAnimal and ReturnDog, thanks to covariance.

Delegates in C# such as Action, Func, and Predicate make it easy to pass methods as parameters, enabling more flexible and modular code.