Phase 11 of 30 · Topic 11.1

Standard .NET Event Pattern & Thread-Safe Invocation

1Concept

Events wrap delegates to prevent external subscribers from overwriting listeners (`=`) instead of adding (`+=`). Thread-safe invocation copies the event reference to a local variable or uses `?.Invoke()`.

2Architecture Diagram

Publisher Instance ──> event EventHandler<OrderEventArgs> OrderPlaced;
                              ├── Subscriber 1 (EmailService.OnOrder)
                              └── Subscriber 2 (AuditService.OnOrder)
                              
Thread-Safe Invocation: OrderPlaced?.Invoke(this, args);

3Code Example

C# 13 & .NET 9
using System;

public class OrderEventArgs : EventArgs
{
    public int OrderId { get; init; }
    public decimal Amount { get; init; }
}

public class OrderProcessor
{
    public event EventHandler<OrderEventArgs>? OrderCompleted;

    public void CompleteOrder(int id, decimal amount)
    {
        Console.WriteLine($"Processing Order #{id}...");
        // Thread-safe event trigger
        OrderCompleted?.Invoke(this, new OrderEventArgs { OrderId = id, Amount = amount });
    }
}

public class EventPatternDemo
{
    public static void Main()
    {
        var processor = new OrderProcessor();
        processor.OrderCompleted += (sender, e) =>
        {
            Console.WriteLine($"[Audit] Order #{e.OrderId} finished for ${e.Amount}");
        };

        processor.CompleteOrder(5501, 299.99m);
    }
}

4Expected Output

Processing Order #5501...
[Audit] Order #5501 finished for $299.99

5Key Takeaways

  • Always inherit custom event data classes from `System.EventArgs`.
  • Use `?.Invoke(this, e)` to avoid race conditions with concurrent unsubscriptions.
  • Always unsubscribe (`-=`) when transient subscribers are disposed.