Declare an event
An event lets an object tell other code that something happened. The object that raises the event is the publisher; here, an oven that finishes baking. Methods that want the news are subscribers, also called event handlers.
Inside class Oven, public event Action? Done; declares an event named Done. Its type, Action, is a delegate type for methods that take nothing and return nothing. The ? means it can be null: no subscribers.
Subscribe
Line 1 makes an Oven object on the heap and stores a reference in oven. Its event Done starts as null: an empty list of handlers. The two empty places on the oven stand for that list.
Line 2, oven.Done += Beep;, subscribes. Beep is a method, so C# turns it into an Action delegate, and += adds it to the event's invocation list: the list of methods to call when the event is raised.
Line 3 adds Flash the same way. Done now holds a multicast delegate: one delegate whose invocation list has two methods, in the order they were added. Beep first, then Flash.
Raise
Line 4 calls oven.Bake(), and Bake runs Done?.Invoke(). That raises the event. The ?. checks for null first: with no subscribers, Done is null and nothing is called, so there is no error.
Invoke calls every method in the invocation list, one after another, in order. Beep prints Beep!, then Flash prints Flash!. The oven did not need to know who was listening.
Unsubscribe and rules
Line 5, oven.Done -= Beep;, unsubscribes: -= removes Beep from the invocation list, and Flash moves up to first place. Removing works because Beep names the same method that was added.
Line 6 bakes again. Now the list holds only Flash, so only Flash! is printed. The whole program printed three lines: Beep!, Flash!, Flash!.
Why the word event? Outside class Oven, code may only use += and -= on Done. Writing oven.Done() or oven.Done = null there is compile error CS0070. Only the oven itself can raise its event.
What happens
// step 1/10 · news publisher the object that raises the event (oven) event the news: "baking is done" (Done) subscribers methods that react: handlers (Beep, Flash) the publisher never needs to know who is listening
Delegates never change. Each += makes a new delegate with a longer invocation list (Delegate.Combine) and stores it in the event; each -= makes a new, shorter one (Delegate.Remove). The figure shows the list being edited in place. Most .NET events follow a standard pattern: the type EventHandler or EventHandler<TEventArgs>, and handlers with two parameters, sender (who raised it) and e (the event data). Action is used here to keep the example short.