C#
delegates
programming
software development
coding

When would you use delegates in C?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In C#, delegates are type-safe references to methods. You use them when behavior needs to be passed around as data, especially for callbacks, event notification, small strategy selection, and APIs that should accept caller-supplied logic without coupling to a concrete class.

Use delegates for callbacks

A delegate is a natural fit when one component needs to call user-provided code later. This keeps the component generic while letting the caller decide what should happen.

csharp
1using System;
2
3class Downloader
4{
5    public void Fetch(string url, Action<string> onCompleted)
6    {
7        string content = $"downloaded: {url}";
8        onCompleted(content);
9    }
10}
11
12class Program
13{
14    static void Main()
15    {
16        var downloader = new Downloader();
17        downloader.Fetch("https://example.com", content => Console.WriteLine(content));
18    }
19}

This pattern is simpler than creating an interface when the only extension point is one method.

Use delegates for events and notifications

C# events are built on delegates. If an object needs to announce that something happened without knowing who is listening, delegates are the mechanism underneath.

csharp
1using System;
2
3class TimerService
4{
5    public event Action? Tick;
6
7    public void RunOnce()
8    {
9        Tick?.Invoke();
10    }
11}
12
13class Program
14{
15    static void Main()
16    {
17        var timer = new TimerService();
18        timer.Tick += () => Console.WriteLine("tick received");
19        timer.RunOnce();
20    }
21}

Events are the right choice when multiple subscribers may react independently. The publisher stays decoupled from subscriber implementations.

Use delegates for lightweight strategy injection

If a method needs one small piece of caller-defined behavior, a delegate is often better than a whole interface hierarchy.

csharp
1using System;
2using System.Collections.Generic;
3
4class Program
5{
6    static IEnumerable<int> Filter(IEnumerable<int> values, Predicate<int> predicate)
7    {
8        foreach (var value in values)
9        {
10            if (predicate(value))
11            {
12                yield return value;
13            }
14        }
15    }
16
17    static void Main()
18    {
19        foreach (var number in Filter(new[] { 1, 2, 3, 4, 5 }, n => n % 2 == 0))
20        {
21            Console.WriteLine(number);
22        }
23    }
24}

This is exactly why the framework includes Action, Func, and Predicate. They cover the most common delegate shapes without making you define a custom delegate type every time.

Use custom delegates when the signature carries meaning

Although Func and Action are convenient, a named delegate can make an API easier to read when the parameter represents a domain concept.

csharp
1using System;
2
3delegate bool OrderRule(decimal total);
4
5class OrderChecker
6{
7    public bool Validate(decimal total, OrderRule rule)
8    {
9        return rule(total);
10    }
11}

A named delegate documents intent more clearly than Func<decimal, bool> when the rule has business meaning.

When not to use delegates

Delegates are not a replacement for every abstraction. If behavior requires stateful collaboration, multiple related methods, or versioned contracts, an interface or class is usually better. Delegates shine when the extension point is narrow and behavior-focused.

For example, a payment provider abstraction probably deserves an interface with explicit methods and dependency injection. A "sort comparison" or "retry callback" usually fits a delegate perfectly.

Lambda expressions make delegates practical

Delegates became much more useful once lambdas made them concise to create. LINQ depends heavily on this model.

csharp
1using System;
2using System.Linq;
3
4class Program
5{
6    static void Main()
7    {
8        var result = new[] { "apple", "pear", "banana" }
9            .Where(item => item.Length > 4)
10            .Select(item => item.ToUpper());
11
12        foreach (var item in result)
13        {
14            Console.WriteLine(item);
15        }
16    }
17}

The Where and Select calls accept delegates, which is why callers can express logic inline without subclassing anything.

Common Pitfalls

  • Defining a custom delegate when Action, Func, or Predicate already expresses the signature clearly.
  • Using delegates for large, stateful collaborations that should be modeled with interfaces or services.
  • Forgetting that multicast delegates call all subscribed methods, which can affect ordering and error handling.
  • Exposing raw delegates where an event would be safer for public notification APIs.
  • Hiding business meaning behind generic delegate types when a named delegate would improve readability.

Summary

  • Use delegates when code needs to accept behavior as an argument.
  • They are especially useful for callbacks, events, and small strategy hooks.
  • Prefer Action, Func, and Predicate for common signatures.
  • Use named delegates when the method shape has domain meaning.
  • Switch to interfaces when the abstraction needs multiple operations or persistent state.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.