Author : MD TAREQ HASSAN | Updated : 2022/01/23

What is a Delegate?

Delegate Syntax

Define delegate type

public delegate double MyDelegate(int x, int y);

// ... ... ...

Create delegate type variable and assign methods

public double FooMethod(int p, int q)
{
    // ... ... ...
}

public double BarMethod(int r, int s)
{
    // ... ... ...
}

// ... ... ...
MyDelegate md = FooMethod;
md += BarMethod;

Now, invoke registered methods

md.Invoke(4, 5);

Usage of Delegate

We can use Action and Func to implement features in which we want to call referenced method without knowing which method will be invoked at compile time. Practical examples:

About Built-in Generic Delegate

Advantages of built-in generic delegates

Action Delegate

There are variations of Action delegate with up to 16 arguments:

Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>

Usage of Action Delegate

Example - writing a message to both console and debug output

Action<string> messageBroker = Console.WriteLine;
messageBroker += Debug.WriteLine;

var message = "This a test message";
messageBroker.Invoke(message);

Example - updating UI on receiving API response

public interface IGithubHttpClient
{
    public void FetchRepositoryList(string githubId, int fetchCount, Action<List<string>> OnReceivedRepos);
}

public class GithubHttpClient : IGithubHttpClient
{
    public async void FetchRepositoryList(string githubId, int fetchCount, Action<List<string>> OnReceivedRepos)
    {

        //
        // For simplicity, simulating API call instead of actual API call
        // (in actual app, Flurl.Http or native HttpClient will be used)
        //
        var twoSeconds = 2000;
        await Task.Delay(twoSeconds);

        var repoList = new List<string>();
        for (int i = 0; i < fetchCount; i++)
        {
            repoList.Add(@"https://github.com/hovermind");
        }

        //
        // Invoke callback
        //
        OnReceivedRepos(repoList);
    }
}

//
// Program.cs
//
IGithubHttpClient githubClient = new GithubHttpClient();

Console.WriteLine("Calling Github API \n");

githubClient
    .FetchRepositoryList(
        githubId: "CodeMazeBlog",
        fetchCount: 3,
        OnReceivedRepos: (repoList) =>
        {
            // Update UI when data is received
            Console.WriteLine($"Received repository list \nRepositoryCount : {repoList.Count}");
        });

Console.WriteLine("When data is being fecthed, perforing other tasks in UI i.e. updating progress bar.\n");

var _ = Console.ReadKey();

Func Delegate

Similar to Action, Func also have variations with up to 16 arguments and the last argument of Func is always return type:

Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>

Usage of Func Delegate

Example - giving user the option to decide type of opertaion they want to perform

//
// user determines what kind of arithmetic operation should be performed by passing a lambda expression
//
public interface IMathService
{
    public string PerformOperation(int x, int y, Func<int, int, double> arithmeticOperation);
}

public class MathService : IMathService
{
    public string PerformOperation(int x, int y, Func<int, int, double> arithmeticOperation)
    {
        var result = arithmeticOperation(x, y);
        var xPercentage = x / result * 100;
        var yPercentage = y / result * 100;
        return $"X: {xPercentage:N2}% and Y: {yPercentage:N2}%";
    }
}

//
// Program.cs
//
IMathService mathService = new MathService();
var resultString = mathService.PerformOperation(2, 8, arithmeticOperation: (x, y) => x + y );
Console.WriteLine(resultString);

Predicate Delegate

Syntax

public delegate bool Predicate<in T>(T obj);

Usage of Predicate Delegate

Example - creating custom LINQ

public static class CustomLinqExtensionMethod
{
    public static IEnumerable<int> Filter(this IEnumerable<int> numberList, Func<int, bool> predicate)
    {
        foreach (var number in numberList)
        {
            if (predicate(number))
            {
                yield return number;
            }
        }
    }
}

//
// Program.cs
//
var numberList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

var evenNumbersOnly = numberList.Filter(number => number % 2 == 0);

Console.WriteLine($"Numbers after filtering: { string.Join(",", evenNumbersOnly) }");

var _ = Console.ReadKey();