A delegate in C# is a type-safe function pointer that can reference methods with a particular signature. It allows methods to be passed as parameters, assigned to variables, and invoked dynamically. Delegates are a fundamental part of building flexible, extensible, and event-driven applications in C#.

Think of a delegate as a contract: any method that matches the signature can be used with the delegate.

🔧 Delegate Syntax

Declare and use a delegate:

// 1. Declare a delegate
public delegate void GreetDelegate(string name);

// 2. Create methods that match the delegate signature
public class Greeter
{
    public void EnglishGreeting(string name)
    {
        Console.WriteLine($"Hello, {name}!");
    }

    public void SpanishGreeting(string name)
    {
        Console.WriteLine($"¡Hola, {name}!");
    }
}

// 3. Use the delegate
class Program
{
    static void Main()
    {
        Greeter greeter = new Greeter();

        GreetDelegate greet = greeter.EnglishGreeting;
        greet("Alice"); // Output: Hello, Alice!
        
        greet = greeter.SpanishGreeting;
        greet("Alice"); // Output: ¡Hola, Alice!
    }
}

🔁 Why Use Delegates?

Delegates help in:

🧰 Built-in Delegate Types

C# provides predefined delegates in the System namespace for convenience:

Action<string> print = Console.WriteLine;
print("Using Action delegate!");

Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(5, 3)); // Output: 8

🌐 What are Multicast Delegates?

A multicast delegate is a delegate that can hold references to more than one method. When invoked, all methods in the invocation list are called in order.

All delegates derived from System.MulticastDelegate support this feature.

🔗 Example of Multicast Delegates

public delegate void Notify(); // No parameters and void return type

class Notifications
{
    public void EmailNotification()
    {
        Console.WriteLine("Email sent.");
    }

    public void SMSNotification()
    {
        Console.WriteLine("SMS sent.");
    }

    public void PushNotification()
    {
        Console.WriteLine("Push notification sent.");
    }
}

class Program
{
    static void Main()
    {
        Notifications notify = new Notifications();

        Notify notifyAll = notify.EmailNotification;
        notifyAll += notify.SMSNotification;
        notifyAll += notify.PushNotification;

        notifyAll(); 
        /*
            Output:
            Email sent.
            SMS sent.
            Push notification sent.
        */
    }
}

➖ Removing Methods from Multicast Delegates

You can remove methods from the invocation list using the -= operator:

notifyAll -= notify.SMSNotification;
notifyAll();
/*
    Output:
    Email sent.
    Push notification sent.
*/

⚠️ Key Notes and Limitations

public delegate int Calculation();

public static int A() { Console.WriteLine("A"); return 1; }
public static int B() { Console.WriteLine("B"); return 2; }

public static void Main()
{
    Calculation calc = A;
    calc += B;

    int result = calc(); // Output: A B, Result: 2 (from B)
    Console.WriteLine("Result: " + result);
}

🎯 When to Use Delegates vs Interfaces vs Events?

Use Case Best Choice
Single method behavior injection Delegate
Polymorphism and multiple methods Interface
Notification to subscribers Event/Delegate

🛠 Real-World Use Cases

🧪 Summary

📝 Final Thoughts

Understanding and mastering delegates is essential for writing clean, maintainable, and extensible C# applications. Whether you're handling events, implementing design patterns, or building libraries, delegates provide the flexibility and type safety you need.