Events
Let objects announce that something happened without knowing who is listening: declaring, raising, subscribing to and unsubscribing from events.
Model class shouldn’t need to know about any of them. Events let one object (the publisher) announce “this happened” and any number of other objects (subscribers) react.
Declaring and raising an event
public class SavedEventArgs : EventArgs{ public string Path { get; } public SavedEventArgs(string path) => Path = path;} public class Model{ public event EventHandler<SavedEventArgs>? Saved; // the event public void Save(string path) { // … write the file … OnSaved(new SavedEventArgs(path)); } protected virtual void OnSaved(SavedEventArgs e) => Saved?.Invoke(this, e); // raise it (if anyone listens)}Subscribing
var model = new Model(); model.Saved += (sender, e) => Console.WriteLine($"Status: saved to {e.Path}");model.Saved += OnSavedBackup; model.Save("tower.ifc"); model.Saved -= OnSavedBackup; // unsubscribemodel.Save("tower-v2.ifc"); static void OnSavedBackup(object? sender, SavedEventArgs e) => Console.WriteLine($"Backup: copying {e.Path}");Status: saved to tower.ifc Backup: copying tower.ifc Status: saved to tower-v2.ifc
The standard pattern
Use EventHandler(no data) orEventHandler<TEventArgs>(with data).Handlers receive (object? sender, TEventArgs e): who raised it, and the details.Event-data classes derive from EventArgsand end inEventArgs.Raise through a protected virtual void OnXxx(…)method so subclasses can extend it.Name events with a verb: Saved,SelectionChanged,Clicked(past tense for “after”,-inglikeClosingfor “before”).
Why “event” and not a public delegate?
event keyword restricts outside code to += and -=. Other classes can’t raise the event themselves or wipe everyone else’s subscriptions with =. Try the two buttons in the diagram to see the errors.
Events in desktop apps
// WPF / WinForms controls expose events for user actionssaveButton.Click += (sender, e) => model.Save(currentPath);membersList.SelectionChanged += OnSelectionChanged;this.Closing += (s, e) => { if (HasUnsavedChanges) e.Cancel = !ConfirmClose(); };Thermometer with a Temperature property. When it is set above 40, raise an event Overheated carrying the temperature. Subscribe a handler that prints a warning and set the temperature to 35, then 42.
Show the solution
var t = new Thermometer();t.Overheated += (_, e) => Console.WriteLine($"Warning: {e.Value} °C!");t.Temperature = 35;t.Temperature = 42; public class ValueEventArgs : EventArgs{ public double Value { get; } public ValueEventArgs(double v) => Value = v;} public class Thermometer{ public event EventHandler<ValueEventArgs>? Overheated; private double _temp; public double Temperature { get => _temp; set { _temp = value; if (value > 40) Overheated?.Invoke(this, new ValueEventArgs(value)); } }}Warning: 42 °C!
Remember
Events let a publisher notify any number of subscribers without knowing them. Declare with event EventHandler<T>; raise withXxx?.Invoke(this, e).Subscribers use +=and-=; outside code can’t raise or reset the event.Unsubscribe when done to avoid memory leaks.