Glossary / Events
Events
An event is a publisher-controlled notification mechanism built on delegate types. Subscribers can add and remove handlers, while only the declaring type can raise the event; this encapsulation prevents consumers from replacing the entire delegate or invoking notifications directly.
Declaration and lifecycle
public event EventHandler<EventArgs>? Changed;
protected virtual void OnChanged() => Changed?.Invoke(this, EventArgs.Empty);
Events use += and -= for subscription. Field-like events synthesize backing storage and thread-safe add/remove accessors, but check-and-invoke is not an atomic guarantee that a subscriber remains attached. Event handlers run synchronously on the raising thread unless the publisher explicitly dispatches them. A slow handler blocks the publisher; exceptions propagate and can prevent later handlers from running.
Long-lived publishers can retain subscribers and create memory leaks if handlers are not removed. Anonymous lambdas are hard to unsubscribe unless stored. Weak-event patterns or scoped subscription tokens can help, but add complexity. Async event handlers (async void) cannot be awaited by the publisher and have special exception behavior; prefer explicit async APIs when completion matters.
Engineering guidance
Document thread, ordering, and exception semantics. Raise events outside locks where possible to avoid reentrancy/deadlocks. Unsubscribe at lifecycle end. Use events for notifications, not request/response or awaited workflows.
Interview questions
- Event vs public delegate field? Event restricts invocation and assignment to the declaring type.
- Are handlers asynchronous by default? No; invocation is synchronous on the raising thread.
- Why can events leak memory? Publisher retains delegates that reference subscribers.
- What is risky about async void handlers? Completion cannot be awaited and exceptions are harder to observe.
- Why raise outside locks? Handlers can reenter or block and create lock coupling/deadlocks.