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.