C# Delegate

Theodorus DoniTheodorus Doni
6 min read

Introduction

Hello all, today I back with another C# topic, and today I will share what i learned about C# Delegate. I hope with this article you will know more about C# Delegate and use it on your project.

What Is It?

A delegate is a type that holds a reference to one or more methods. As a 'method holder', it enables methods to be stored and called dynamically, making it ideal for callbacks and event-driven programming.

A callback is a method that is called after a certain operation completes to perform a specific task.

Why Implement It?

There are several pros and cons to consider before implement an delegate in our project. Here are some of them:

Pros

  • Enhance code reusability.

  • Enhance the clarity of the code flow.

  • Minimize dependencies on other classes.

Cons

  • It may be challenging for new programmers to understand.

  • Having too many methods assigned to a delegate could potentially cause performance overhead.

How It Works?

To understand how delegate work in the system, let's implement it first. To implement a delegate, all you need to do is use delegate keyword. However, starting .NET Framework 3.5, you can also use an Action, Func or Predicate keywords which are found in the System namespace, as shown in the code below:

using System;

namespace Lncodes.Example.Delegate;

public sealed class InventoryController
{
    private readonly bool _isReachMaxInventoryCapacity = false;

    public void AddItem(Action successAddCallback, Action failAddCallback)
    {
        Console.WriteLine("Adding Item");
        if (_isReachMaxInventoryCapacity) failAddCallback();
        else successAddCallback();
    }
}

public static class Program
{
    private static void Main()
    {
        var inventoryController = new InventoryController();
        inventoryController.AddItem(SuccessAddCallback, FailAddCallback);
    }

    private static void SuccessAddCallback() =>
        Console.WriteLine("Successfully added item to the inventory.");

    private static void FailAddCallback() =>
        Console.WriteLine("Failed to add item to inventory as it has reached its maximum capacity.");
}

In the code above, I have created delegates as parameters of the AddingItem() method. These delegates are used as callbacks that get invoked every time an item is successfully added to the inventory or if it fails to be added. After implementing these delegates in the code, here's what will happen in the system:

  1. When a method is assigned to a delegate, the delegate holds a reference to that method.

  2. When assigning a method to a delegate, the method’s parameters and return type must exactly match the delegate’s, or it will cause a compiler error.

  3. When a delegate is called, it calls all the methods assigned to it sequentially.

Console Application

In my console application, delegates are used to manage callbacks for inventory actions. Whenever an item is added or removed from the inventory, a delegate is called to handle the action.

Below is a list of the classes I used to create this console application, along with a brief explanation of each class:

ClassDescription
InventoryControllerThis class is used to manage inventory-related operations such as adding and deleting items.
ProgramThis class is used to display the delegate callbacks from inventory operations on the console.

In the video above, the console application displays four different statuses after adding or deleting items in the inventory. Each status represents the use of a different type of delegate. Below is a list of all the delegate types used in this console application, along with a brief explanation of each delegate:

Delegate TypesDescription
ActionThis delegate is used to display the success status after adding the potion item to the inventory. The output can be seen in console message number 1.
FuncThis delegate is used to get a random item to be added to the inventory. The output can be seen in console message number 2.
PredicateThis delegate is used to check if the inventory has reached its maximum capacity. The output can be seen in console message number 3.
DelegateThis delegate is used to display the success status after deleting an item from the inventory. The output can be seen in console message number 4.

By using delegates, managing and handling inventory action callbacks becomes more efficient. Different delegate types for various inventory action callbacks enhance code clarity and ensure type safety. They also make the code more flexible and easier to maintain.

The source code for this console application can be viewed and downloaded at Project Repository – Github.

Additional Information

I have discovered some additional information about C# Delegate. If you have any additional information regarding C# Delegate that you'd like to share, please feel free to leave a comment.

Naming Guidelines

When implementing delegates, following specific naming guidelines can help ensure code readability and consistency. Here are some naming conventions recommended by Microsoft:

  • Do use 'Callback' suffix for delegates that returns void type e.g. Action successAddCallback.

  • Do use 'Get' prefix for delegates that don't returns a bool type e.g. Func<int> getRandomItemId.

  • Do use 'Is', 'Can', or 'Has' prefix for delegates that return bool type e.g. Predicate<int> canAddingItems.

  • Don’t use 'Delegate' suffix for delegate names e.g. delegate void DeleteItemDelegate().

Action VS Func VS Predicate

Action, Func, and Predicate are all delegate types that offer similar functionalities for managing method references. Due to these similarities, choosing between them can be challenging. Here are some key factors to consider before making a decision:

  • An Action delegate is a delegate that returns no value (void), typically used for callbacks or situations where an action needs to be executed without producing a result. It's equivalent to delegate void OnComplete().

  • A Func<TReturn> delegate is a delegate that returns a value with specified type, typically used for operations that produce a result. It's equivalent to delegate TReturn OnComplete().

  • A Predicate<TInput> delegate is a delegate that returns a bool type, typically used for validating input or determining whether an item meets certain criteria. It's equivalent to delegate bool OnComplete(TInput input).

Further Discoveries

Here are some further discoveries I have gathered from various sources that may provide a deeper understanding of C# Delegate:

  • A delegate can be called using the Invoke method or directly as if it were a regular method.

  • A delegate that holds more than one method is known as a 'Multicast Delegate'.

  • Use the ?. operator to check a nullable delegate e.g. addingItemCallback?.Invoke();. This practice prevents runtime errors by ensuring the delegate is only called when it’s not null.

  • Use the delegate keyword instead of Action, Func, or Predicate when creating a delegate with three or more parameters. This is because Action, Func, and Predicate don't allow naming their parameters, which can make the code harder to read. For example: Action<int, int, int> vs. delegate void(int health, int damage, int speed).

  • Convert a 'Multicast Delegate' into an event if the methods assigned to it are defined outside the class declaration. Declaring it as an event ensures that the delegate can't be called from outside the class declaration, which enhances encapsulation and control over its usage.

    💡
    To learn more about C# Event, check out my post at C# Event - Last Night Codes.

Reference

  1. C# Delegate – Microsoft

  2. C# Action – Microsoft

  3. C# Func – Microsoft

  4. C# Predicate – Microsoft

  5. C# Delegate Naming Guidelines – Microsoft

0
Subscribe to my newsletter

Read articles from Theodorus Doni directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Theodorus Doni
Theodorus Doni