Delegates and Events in C#: Callbacks & Publisher-Subscriber Pattern

1. Delegates and Events Overview

Q: What is a delegate in C#?

A delegate is a type-safe function pointer that defines a method signature, allowing methods to be passed as parameters or assigned to variables. Delegates enable flexible, decoupled code, often used for callbacks and event handling in C#. They are defined using the delegate keyword and typically reside in the System namespace (e.g., Action, Func).

Q: What is an event in C#?

An event is a mechanism that allows a class to notify other classes or objects when something occurs. Events are built on delegates, providing a publisher-subscriber model where a class (publisher) raises an event, and other classes (subscribers) handle it using event handlers.

Q: Why are delegates and events important?

Q: How do delegates and events differ from C/C++?

2. Delegates Syntax and Usage

Q: What is the syntax for declaring a delegate in C#?

A delegate is declared using the delegate keyword, specifying a return type and parameter list.

Syntax:

delegate returnType DelegateName(parameterList);

Q: How do you use delegates in C#?

Delegates can be:

C# also provides built-in delegates:

Q: What are common use cases for delegates?

Q: Can you give an example of delegates in C#?

using System;

namespace DelegatesExample
{
    // Declare a delegate
    delegate void MessageDelegate(string message);

    class Program
    {
        // Methods matching delegate signature
        static void PrintMessage(string message)
        {
            Console.WriteLine($"Print: {message}");
        }

        static void LogMessage(string message)
        {
            Console.WriteLine($"Log: {message}");
        }

        static void Main(string[] args)
        {
            // Instantiate delegate
            MessageDelegate del = PrintMessage;

            // Invoke delegate
            del("Hello, Delegate!");

            // Multicast delegate
            del += LogMessage;
            del("Multicast test");

            // Using built-in Action delegate
            Action<string> action = m => Console.WriteLine($"Action: {m}");
            action("Using Action delegate");

            // Using Func delegate
            Func<int, int, int> add = (a, b) => a + b;
            Console.WriteLine($"Func result: {add(5, 3)}");
        }
    }
}

Output:

Print: Hello, Delegate!
Print: Multicast test
Log: Multicast test
Action: Using Action delegate
Func result: 8

3. Events and Event Handlers

Q: What is the syntax for declaring an event in C#?

An event is declared using the event keyword with a delegate type. Event handlers (methods) are subscribed using += and unsubscribed using -=.

Syntax:

event DelegateType EventName;

Q: How do events and event handlers work in C#?

C# provides a standard event pattern with EventHandler or EventHandler<TEventArgs>.

Q: What is the standard event pattern in C#?

The standard pattern uses EventHandler (for no custom data) or EventHandler<TEventArgs> (for custom data), where:

Q: Can you give an example of events and event handlers in C#?

using System;

namespace EventsExample
{
    // Custom EventArgs for event data
    class TemperatureChangedEventArgs : EventArgs
    {
        public double NewTemperature { get; }
        public TemperatureChangedEventArgs(double temp)
        {
            NewTemperature = temp;
        }
    }

    // Publisher class
    class Thermostat
    {
        // Declare event using EventHandler<T>
        public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;

        private double temperature;
        public double Temperature
        {
            get => temperature;
            set
            {
                temperature = value;
                // Raise event
                TemperatureChanged?.Invoke(this, new TemperatureChangedEventArgs(temperature));
            }
        }
    }

    // Subscriber class
    class Display
    {
        public void Subscribe(Thermostat thermostat)
        {
            thermostat.TemperatureChanged += Thermostat_TemperatureChanged;
        }

        private void Thermostat_TemperatureChanged(object sender, TemperatureChangedEventArgs e)
        {
            Console.WriteLine($"Temperature changed to: {e.NewTemperature:F1}°C");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Thermostat thermostat = new Thermostat();
            Display display = new Display();

            // Subscribe to event
            display.Subscribe(thermostat);

            // Trigger event by changing temperature
            thermostat.Temperature = 22.5;
            thermostat.Temperature = 25.0;

            // Unsubscribe (optional)
            thermostat.TemperatureChanged -= display.Thermostat_TemperatureChanged;
            thermostat.Temperature = 30.0; // No output, as unsubscribed
        }
    }
}

Output:

Temperature changed to: 22.5°C
Temperature changed to: 25.0°C

Q: How do events differ from C/C++?

4. Common Mistakes & Best Practices

Q: Common mistakes?

Delegates:

Events:

Q: Best practices?

Delegates:

Events:

General: