Page Content

Tutorials

Delegates In C#: Enabling Flexible and Extensible Code

In C#, delegate is a key idea in object-oriented programming. It provides a strong mechanism for type-safe function pointers, which allow methods to be viewed as parameters. They are essential to event handling and enable code designs that are flexible and decoupled.

What is a Delegates in C#?

Reference TypeA reference type called a delegate contains a method reference. This reference may be pointing to a static method or an instance method.

Type-Safe Function Pointers: In contrast to C language/C++ function pointers, C# delegates are type-safe. This means that in order to avoid compiler errors, the delegate’s signature (return type and parameters) must exactly match the signature of the method it points to.

Object-Oriented: The System is the implicit source of delegate structures, which are object-oriented.Distribute the class. They can call methods of any object as long as the signatures match, and they encapsulate a callable entity.

Sealed and Immutable: Custom classes cannot be derived from System because delegate types are sealed.Give it away. The method that a delegate object points to cannot be altered once it has been formed and linked to a method; however, the delegate variable itself can be reassigned to a new delegate object that points to a different method (provided that the signatures match).

Why Use Delegates?

There are several important uses for delegates:

Callback Methods: They make callback functions possible by allowing a method to be provided as an argument to another method. By doing this, the caller is separated from the particular implementation of the method being invoked.

Event Handling: In C#, events are based on delegates. They offer a notification pattern that allows one class (publisher) to alert other classes (subscribers) to interesting events, such as button clicks.

Decoupling and Generic Behaviors: Delegates facilitate loose coupling by enabling code to use any method that satisfies a particular signature without requiring knowledge of the method’s precise name or class. For general algorithms like sorting, where the comparison logic can be supplied as a delegate, this is especially helpful.

LINQ and Lambda Expressions: An essential component of Lambda expressions, which offer a succinct syntax for constructing anonymous methods frequently used with LINQ queries, are delegate functions.

Steps to Create and Use Delegates

Four steps are usually involved in creating and using delegates:

Delegate Declaration: You describe the parameters and return type that methods compatible with this delegate must have when you define the delegate type.

Within a class, outside of all classes, or as a top-level object in a namespace, delegate declarations are possible. Public, protected, internal, private, and new are several modifiers that can regulate its accessibility.

Delegate Method Definition (or Assignment): Give specifics about the method (or methods) that the delegate will use. The signatures of this method and the delegate must be identical.

Methods might be lambda expressions, anonymous methods, or named methods.

Delegate Instantiation: Make an instance of the delegate type and link it to a certain function.

In C# 2.0 and later, you can alternatively use a simpler syntax that does not require the new keyword or the name of the delegate type.

Delegate Invocation: Invoke the delegate instance, which then calls the method or methods it specifies.

The Invoke() method and the () operator are two ways to call delegate functions.

Types of Delegates

Two primary categories of delegates exist:

Single-Cast (Unicast) Delegate: This kind of delegate only invokes and points to one method. The aforementioned example illustrates single-cast delegates using nc1 and nc2 separately.

Multicast Delegate: Multiple methods can be invoked and pointed to by this delegate. The + operator is used to combine delegate instances, and the – operator is used to delete them. Every method in the multicast delegate’s invocation list is called sequentially when the delegate is invoked.

Return Type: When a multicast delegate has a non-void return type, previous return values are deleted and only the return value from the final method called in the list is sent to the caller. Multicast delegates so usually have a void return type.

Output Parameters: The reference is sent sequentially to each method if a delegate utilises the ref or out arguments, and any modifications made by one method are visible to the next. Nevertheless, it is not advised to use out parameters with multicast event delegates because it is uncertain which delegate’s value would be returned.

Built-in Delegates

In the System namespace, Microsoft offers a number of built-in generic delegate types to make typical situations easier:

Action<T...>: Represents a method that returns a value (void) when three or more input parameters are entered. A method that accepts no parameters and returns void is called an action (without generic parameters).

Func<T..., TResult>: Signifies a procedure that yields a value of type TResult after accepting zero or more input parameters. A method that takes no parameters and returns TResult is called func.

Predicate<T>: A specialized Func that denotes a method that returns a boolean value after accepting a single input parameter of type T. It is similar to Func in that it is used to specify custom criteria.

EventHandler and EventHandler<TEventArgs>: For managing events, these built-in delegates are standard. EventHandler enables custom event data via TEventArgs, whereas EventHandler is for events without special data.

Advanced Features

Covariance and Contravariance: These ideas offer flexibility in matching delegate types to method signatures, particularly when using generic delegates.

  • Covariance (out): Permits a method to have a more derived return type than what the delegate specifies.
  • Contravariance (in): Lets a method have more generic (less derived) parameter types than what the delegate specifies.
  • Co- and contravariance with multicast delegates (such as event handlers) is supported, although it should be used with caution as it may cause problems if runtime types are different from compile-time types.

Anonymous Methods and Lambda Expressions: Without requiring a distinct named method, these offer a succinct syntax for defining methods inline. Lambda expressions are frequently used with LINQ queries and are a shorthand for delegate initialisation.

Closures: Lambda expressions and anonymous methods are capable of capturing “outer variables” from the enclosing scope. The delegate can still access these captured variables (closures) after the originating method has completed running.

Events as Encapsulated Delegates: In essence, an event is a condensed delegate. The signature that subscribers will use for the event handler method is defined by the delegate. The event delegate is invoked by publishers to raise events.

Consider delegates as a kind of universal remote control. You may use a universal remote to control your TV, DVD player, and sound system instead of having separate remote controls for each. Although the delegate instance, which is represented by each button on the remote, is not the actual device, it is aware of which device to transmit the instruction to and how to do so, provided that the device comprehends the command (matches the delegate’s signature). It can be configured to control numerous devices (multicast delegate), allowing each device to react differently with a single button click.

Code example

using System;
// 1. Delegate Declaration:
// This line declares a delegate type named 'NumberChanger'.
// It specifies that any method associated with this delegate must:
// - Take a single integer (int n) as an argument.
// - Return an integer (int) value.
delegate int NumberChanger(int n);
namespace DelegateExample
{
    class Program
    {
        // A static integer variable that our delegate methods will modify.
        static int currentValue = 10;
        // 2. Delegate Method Definition (matching the delegate's signature):
        // This method adds a value to 'currentValue' and returns the new sum.
        public static int AddNumber(int p)
        {
            currentValue += p;
            return currentValue;
        }
        // Another method matching the delegate's signature:
        // This method multiplies 'currentValue' by a factor and returns the result.
        public static int MultiplyNumber(int q)
        {
            currentValue *= q;
            return currentValue;
        }
        // A helper method to display the current static value.
        public static int GetCurrentValue()
        {
            return currentValue;
        }
        static void Main(string[] args)
        {
            // 3. Delegate Instantiation:
            // We create instances of our 'NumberChanger' delegate.
            // 'ncAdd' is associated with the 'AddNumber' method.
            NumberChanger ncAdd = new NumberChanger(AddNumber);
            // 'ncMultiply' is associated with the 'MultiplyNumber' method.
            NumberChanger ncMultiply = new NumberChanger(MultiplyNumber);
            // 4. Delegate Invocation:
            // We call the delegate instances as if they were the methods themselves.
            ncAdd(25); // This invokes the AddNumber method with 25.
            Console.WriteLine("Value after adding: {0}", GetCurrentValue()); 
            ncMultiply(5); // This invokes the MultiplyNumber method with 5.
            Console.WriteLine("Value after multiplying: {0}", GetCurrentValue()); 
            Console.WriteLine("\nPress any key to exit.");
            Console.ReadKey();
        }
    }
}

Output

Value after adding: 35
Value after multiplying: 175
Press any key to exit.

You can also read Exception Handling In C#: Creating Your Own Error Types

Agarapu Geetha
Agarapu Geetha
My name is Agarapu Geetha, a B.Com graduate with a strong passion for technology and innovation. I work as a content writer at Govindhtech, where I dedicate myself to exploring and publishing the latest updates in the world of tech.
Index