;

C# Delegates


Delegates in C# are a unique and powerful feature that lets you store references to methods, enabling flexible, reusable, and event-driven code. They serve as method pointers, allowing you to pass methods as parameters, making your applications highly dynamic and modular. In this tutorial, we'll walk through the types of delegates available in C#, their syntax, use cases, and practical real-world applications.

What Are Delegates?

A delegate in C# is a type-safe object that points to one or more methods. In simple terms, a delegate acts as a reference to a method, allowing you to encapsulate a method's signature (return type and parameters) and assign any method that matches this signature to the delegate. This is invaluable for creating flexible, callback-style functionality.

Defining and Using Delegates

Basic Syntax of Delegates

To define a delegate, you use the delegate keyword, followed by a return type and a signature (parameter list). Here’s a simple example:

// Declare a delegate
public delegate void Notify(string message);

class Program
{
    static void DisplayMessage(string message)
    {
        Console.WriteLine(message);
    }

    static void Main()
    {
        // Instantiate the delegate
        Notify notifier = DisplayMessage;
        
        // Call the delegate
        notifier("Hello, Delegate!");
    }
}

In this example:

  • Notify is a delegate that points to any method matching the void(string) signature.
  • DisplayMessage is assigned to notifier, allowing you to call DisplayMessage through notifier.

Types of Delegates

Delegates can be categorized into single-cast, multi-cast, and generic delegates. Let’s examine each type in more detail.

Single-cast Delegates

A single-cast delegate references only one method at a time. It’s straightforward and provides a mechanism to point to a single method:

public delegate void DisplayDelegate(string message);

class Program
{
    static void ShowMessage(string message)
    {
        Console.WriteLine("Message: " + message);
    }

    static void Main()
    {
        DisplayDelegate display = ShowMessage;
        display("Hello from single-cast delegate!");
    }
}

Multi-cast Delegates

Multi-cast delegates can hold references to more than one method. When invoked, each method in the delegate’s invocation list is called in sequence.

public delegate void Logger();

class Program
{
    static void LogToFile()
    {
        Console.WriteLine("Logging to file...");
    }

    static void LogToDatabase()
    {
        Console.WriteLine("Logging to database...");
    }

    static void Main()
    {
        Logger logger = LogToFile;
        logger += LogToDatabase;
        
        logger(); // Calls both LogToFile and LogToDatabase
    }
}

Use Case: Multi-cast delegates are beneficial in event handling, where multiple methods need to respond to a single event.

Generic Delegates

C# provides generic delegates Func, Action, and Predicate for flexible, reusable delegate types:

  • Func: Can represent any method that returns a value.
  • Action: Represents any method that doesn’t return a value (void).
  • Predicate: Represents any method that returns a bool.
class Program
{
    static void Main()
    {
        Func<int, int, int> add = (x, y) => x + y;
        Console.WriteLine(add(2, 3)); // Outputs 5
        
        Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
        greet("Alice");
        
        Predicate<int> isPositive = num => num > 0;
        Console.WriteLine(isPositive(5)); // Outputs True
    }
}

Use Case: Generic delegates are ideal for passing methods without declaring custom delegate types.

Real-World Example: Logger System

Let’s create a real-world scenario where we want to implement a logger that can log messages to different outputs, such as console, file, and a database. With delegates, we can define flexible logging mechanisms.

Example

using System;
using System.Collections.Generic;

public delegate void Logger(string message);

class LoggerSystem
{
    public static void LogToConsole(string message)
    {
        Console.WriteLine("Console Log: " + message);
    }

    public static void LogToFile(string message)
    {
        Console.WriteLine("File Log: " + message);
    }

    public static void LogToDatabase(string message)
    {
        Console.WriteLine("Database Log: " + message);
    }
}

class Program
{
    static void Main()
    {
        Logger logger = LoggerSystem.LogToConsole;
        logger += LoggerSystem.LogToFile;
        logger += LoggerSystem.LogToDatabase;
        
        logger("This is a multi-cast delegate log message");
    }
}

Explanation

  • Logger: A delegate type that matches any method taking a single string parameter.
  • Logger Methods: Methods that log to different sources (console, file, database).
  • Multi-cast Delegate: logger combines multiple logging methods, so when it’s called, all methods in the invocation list are executed.

Real-World Use Case: In logging systems, multi-cast delegates are advantageous for scenarios where logs need to be sent to multiple outputs at once, enabling an extensible, pluggable architecture.

Key Takeaways

  • Dynamic Method Invocation: Delegates allow you to pass and invoke methods dynamically, making your code more flexible and modular.
  • Multi-casting Capabilities: You can use multi-cast delegates to chain methods, particularly useful in event handling.
  • Reusable Generic Delegates: Func, Action, and Predicate generic delegates offer reusable solutions for standard method signatures.
  • Event-Driven Programming: Delegates are foundational in event-driven programming, providing the basis for event handling in C#.

Summary

Delegates are an essential feature of C#, providing a flexible mechanism for referencing methods. They allow developers to implement callbacks, event handling, and multi-casting, supporting dynamic and modular programming. By using different types of delegates—single-cast, multi-cast, and generic—you can handle various use cases, from simple method references to complex, multi-method event handling.