Phase 10 of 30 · Topic 10.5

Delegate Allocation Traps in High-Throughput Pipelines

1Concept

Passing method groups (`list.Select(MyMethod)`) allocates a new delegate object on every call. Cache the delegate in a static field or use static lambda expressions to achieve zero allocations.

2Architecture Diagram

Method Group in Loop:
for (...) { Process(HelperMethod); } ──> Allocates `new Action()` on every iteration (GC Pressure!)

Cached Static Delegate:
private static readonly Action Cached = HelperMethod;
for (...) { Process(Cached); } ──> ZERO Allocations!

3Code Example

C# 13 & .NET 9
using System;

public class DelegateCacheDemo
{
    private static readonly Func<int, bool> CachedPredicate = static x => x > 0;

    public static void Main()
    {
        int[] data = [-5, 10, -2, 30, 45];

        // Zero-allocation filtering using cached delegate
        int count = 0;
        foreach (int item in data)
        {
            if (CachedPredicate(item)) count++;
        }

        Console.WriteLine($"Positive Items (Zero Allocation): {count}");
    }
}

4Expected Output

Positive Items (Zero Allocation): 3

5Key Takeaways

  • Avoid method group conversions in tight hot loops.
  • Use `static readonly` fields to cache reusable delegates.
  • Check heap allocations with `BenchmarkDotNet` [MemoryDiagnoser].