C# Delegate
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.
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:
When a method is assigned to a delegate, the delegate holds a reference to that method.
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.
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:
Class | Description |
InventoryController | This class is used to manage inventory-related operations such as adding and deleting items. |
Program | This 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 Types | Description |
Action | This 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. |
Func | This delegate is used to get a random item to be added to the inventory. The output can be seen in console message number 2. |
Predicate | This delegate is used to check if the inventory has reached its maximum capacity. The output can be seen in console message number 3. |
Delegate | This 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.
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 todelegate 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 todelegate TReturn OnComplete()
.A
Predicate<TInput>
delegate is a delegate that returns abool
type, typically used for validating input or determining whether an item meets certain criteria. It's equivalent todelegate 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 notnull
.Use the
delegate
keyword instead ofAction
,Func
, orPredicate
when creating a delegate with three or more parameters. This is becauseAction
,Func
, andPredicate
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
Subscribe to my newsletter
Read articles from Theodorus Doni directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by