Delegates Events
By the end of this lesson you'll be able to treat a method like data — store it in a variable, pass it to another method, and chain several together. Then you'll use that power to build events : the publish/subscribe pattern that drives buttons, timers, and notifications across real C# apps.
Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.
Part of the free C# course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
A delegate is like writing down someone's phone number . The number isn't the call itself — it's a way to reach a person later. You can hand that number to a friend, store it for tomorrow, or save several numbers and ring them all at once (a multicast delegate). An event is like a subscription list for a newsletter: anyone can sign up ( += ) or cancel ( -= ), and when the publisher sends an issue, everyone on the list gets notified — but the publisher never needs to know who's reading. You'll build exactly that pattern in this lesson.
Running C# Locally: Install the .NET SDK or use dotnetfiddle.net to run every example below.
Rule of thumb: reach for Action / Func / Predicate first — you'll rarely need to declare your own delegate . Use an event when you want to broadcast a notification.
1. Delegates — Methods as Values
A delegate is a type that can hold a reference to a method, as long as that method has a matching signature (the same parameter types and return type). Once a method is in a delegate variable, you can store it, re-point it, or pass it to another method — exactly like passing a number or a string. That last trick, passing a method as an argument, is what makes delegates so powerful: a method can take behaviour from its caller. Read this worked example, run it, then you'll write your own.
Your turn. The program below uses Func int, int, int — the built-in delegate for "two ints in, one int out". Fill in the two ___ blanks to assign a lambda and invoke it, then run it.
2. Action, Func & Predicate
You almost never need to declare a custom delegate anymore, because C# ships three generic ones that cover nearly every case. Action is for methods that return nothing ( void ). Func is for methods that return a value — the last type parameter is always the return type, so Func int, int, int means "two ints in, an int out". Predicate is a special Func that always returns bool — a yes/no test. Combined with lambdas (the => shorthand for a tiny inline method), they let you pass behaviour around with almost no ceremony.
3. Multicast Delegates
A single delegate variable can hold several methods at once — that's a multicast delegate . You add a method to the chain with += and remove one with -= . When you invoke the delegate, every method runs in turn, in the order they were added. This is the exact mechanism that events are built on, so understanding it here makes the next section click instantly.
4. Events — the Publish/Subscribe Pattern
An event is a multicast delegate with guard rails. It lets a class — the publisher — announce that something happened, while any number of subscribers react however they want. The key difference from a plain delegate is encapsulation: outside code can only += (subscribe) or -= (unsubscribe); only the publishing class can actually raise the event. The standard shape uses the EventHandler T delegate, which always passes a sender (who raised it) and an EventArgs object (the details). Read the worked example, then you'll wire up your own event.
Now you try. The Button class below needs you to (1) raise its Clicked event safely and (2) subscribe a handler to it. Fill in the two blanks marked ___ , then run it.
A plain public delegate field would let any outside code overwrite it with = , wiping out everyone else's subscriptions, or even invoke it whenever they felt like it. The event keyword closes both holes: from outside the class you can only add ( += ) or remove ( -= ) handlers — never assign with = and never raise it. That guarantee is the whole point of events.
Convention: name events with a verb describing what happened — Clicked , OrderPlaced , Finished — and name handler methods On<Event> like OnClicked .
Q: What's the actual difference between a delegate and an event?
An event is a delegate underneath, but with access restrictions. Outside the declaring class you can only subscribe ( += ) or unsubscribe ( -= ) — you can't assign it with = or invoke it. That protection is why you use events for the publish/subscribe pattern and plain delegates for simple callbacks.
Q: When do I use Action vs Func vs Predicate ?
Use Action when the method returns nothing, Func when it returns a value (the last type parameter is the return type), and Predicate T as a tidy name for a Func T, bool — a yes/no test on one value.
Q: Why does raising my event throw a NullReferenceException?
An event with no subscribers is null . Raising it directly crashes. Always use the null-conditional operator: MyEvent?.Invoke(this, e); — it simply skips the call when nobody is listening.
Q: What's a lambda, and is it the same as a delegate?
A lambda — (a, b) => a + b — is a compact way to write a method inline. It isn't a delegate itself, but C# converts it into one to match an Action , Func , or custom delegate. It's just the most concise way to fill a delegate.
No blanks this time — just a brief and an outline. Build a Stopwatch class that exposes a Finished event, ticks for a given number of seconds, then raises the event so a subscriber can react. Wire it up in Main with += , then run it and check your output against the expected lines in the comments.
Practice quiz
What is a delegate in C#?
- A loop construct
- A kind of collection
- A type that holds a reference to a method
- A keyword for inheritance
Answer: A type that holds a reference to a method. A delegate is a type that stores a reference to a method, so you can pass a method around like data.
What must a method have to be assignable to a particular delegate?
- A matching signature — the same parameter types and return type
- The same name as the delegate
- The static keyword
- A public access modifier
Answer: A matching signature — the same parameter types and return type. A method can be stored in a delegate only if its signature (parameter types and return type) matches the delegate's.
Which built-in delegate is for a method that returns nothing (void)?
- Func<T>
- Predicate<T>
- EventHandler
- Action<T>
Answer: Action<T>. Action<T> represents a method that returns void. Func returns a value; Predicate returns bool.
In Func<int, int, int>, which type is the return type?
- The first type parameter
- The last type parameter
- It has no return type
- All of them combined
Answer: The last type parameter. In Func<...>, the last type parameter is always the return type, so Func<int,int,int> means 'two ints in, an int out'.
What does Predicate<T> always return?
- bool
- void
- int
- string
Answer: bool. Predicate<T> is a special Func that always returns bool — a yes/no test on one value.
What is a lambda such as (a, b) => a + b?
- A delegate type declaration
- A new class definition
- A concise way to write a method inline, which C# converts into a delegate instance
- A loop
Answer: A concise way to write a method inline, which C# converts into a delegate instance. A lambda is a compact way to write a method inline; it isn't a delegate itself, but C# converts it into one to match the target type.
What is the difference between a delegate field and an event?
- An event runs faster
- Outside the declaring class, an event can only be subscribed to (+=) or unsubscribed from (-=) — not assigned with = or invoked
- An event cannot have subscribers
- There is no difference
Answer: Outside the declaring class, an event can only be subscribed to (+=) or unsubscribed from (-=) — not assigned with = or invoked. An event is a delegate with access restrictions: outside code can only += / -=, while only the declaring class can raise it or use =.
Which is the standard generic delegate for events, passing a sender and event data?
- Action<T>
- Func<T>
- Predicate<T>
- EventHandler<T>
Answer: EventHandler<T>. EventHandler<T> is the standard event delegate; it passes (object sender, T e) where e is an EventArgs subclass.
How should you raise an event safely when it may have no subscribers?
- MyEvent(this, e);
- MyEvent?.Invoke(this, e);
- MyEvent.Raise();
- new MyEvent();
Answer: MyEvent?.Invoke(this, e);. An event with no subscribers is null, so raising it directly throws. The null-conditional ?.Invoke skips the call when nobody is listening.
Why can a forgotten -= on a long-lived publisher cause a memory leak?
- Events use too much CPU
- Delegates are never collected
- The publisher still references the subscriber, so it can't be garbage-collected
- It corrupts the heap
Answer: The publisher still references the subscriber, so it can't be garbage-collected. If a subscriber never unsubscribes, the publisher keeps a reference to it, preventing garbage collection — a classic leak. Unsubscribe with -= (same handler reference).