Raising events safely in C Sharp (C#) - Time & Space Complexity
When raising events safely in C#, we want to know how the time to notify listeners grows as more listeners subscribe.
We ask: How does the number of event subscribers affect the time it takes to raise an event?
Analyze the time complexity of the following code snippet.
public event EventHandler? MyEvent;
public void RaiseEvent()
{
var handler = MyEvent;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
This code raises an event by calling all subscribed listeners safely using a local copy.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Invoking each subscribed event handler one by one.
- How many times: Once for each subscriber currently attached to the event.
When you have more subscribers, the event must call each one, so the work grows with the number of subscribers.
| Input Size (number of subscribers) | Approx. Operations (calls) |
|---|---|
| 10 | 10 calls |
| 100 | 100 calls |
| 1000 | 1000 calls |
Pattern observation: The number of calls grows directly with the number of subscribers.
Time Complexity: O(n)
This means the time to raise the event grows linearly with the number of subscribers.
[X] Wrong: "Raising an event always takes constant time regardless of subscribers."
[OK] Correct: Each subscriber is called in turn, so more subscribers mean more calls and more time.
Understanding how event raising scales helps you write responsive and efficient code when many listeners are involved.
What if we changed the event to raise handlers asynchronously? How would the time complexity change?
Practice
Solution
Step 1: Understand event raising basics
Raising an event without handlers can cause a null reference error.Step 2: Identify the purpose of safe raising
Safe raising checks if handlers exist before invoking to prevent errors.Final Answer:
To avoid errors when no event handlers are attached -> Option BQuick Check:
Safe event raising prevents null errors [OK]
- Assuming events always have handlers
- Not checking for null before invoking
- Confusing event raising with event subscription
Solution
Step 1: Recall null-conditional operator usage
The ?. operator safely calls a method only if the delegate is not null.Step 2: Match syntax for safe event raising
Using MyEvent?.Invoke(...) safely raises the event without null checks.Final Answer:
MyEvent?.Invoke(this, EventArgs.Empty); -> Option CQuick Check:
?.Invoke is the safe call syntax [OK]
- Forgetting the ?. operator and causing null exceptions
- Using Invoke without null checks
- Using if-check but missing parentheses
public class Publisher {
public event EventHandler? MyEvent;
public void Raise() {
var handler = MyEvent;
if (handler != null) handler(this, EventArgs.Empty);
else Console.WriteLine("No subscribers");
}
}
var pub = new Publisher();
pub.Raise();Solution
Step 1: Analyze event subscription
No handlers are attached to MyEvent before calling Raise.Step 2: Check event invocation logic
Since handler is null, the else branch prints "No subscribers".Final Answer:
No subscribers -> Option AQuick Check:
No handlers means else branch runs [OK]
- Expecting event to raise without subscribers
- Assuming null check is unnecessary
- Confusing event invocation with subscription
public event EventHandler? MyEvent;
public void Raise() {
if (MyEvent != null)
MyEvent(this, EventArgs.Empty);
}Solution
Step 1: Understand event invocation timing
Between null check and invocation, handlers can unsubscribe causing null reference.Step 2: Identify race condition risk
Copying delegate to local variable avoids this race condition.Final Answer:
Possible race condition if handlers change after null check -> Option DQuick Check:
Null check alone is not thread-safe [OK]
- Ignoring race conditions in multithreaded code
- Assuming null check guarantees safety
- Thinking Invoke() method is mandatory
public event EventHandler? MyEvent;
public void Raise() {
var handler = MyEvent;
if (handler != null) {
handler(this, EventArgs.Empty);
Console.WriteLine("Event raised");
} else {
Console.WriteLine("No subscribers");
}
}Solution
Step 1: Check safe event raising
Copying MyEvent to handler ensures safe invocation without race conditions.Step 2: Verify logging placement
Logging after invocation confirms event was raised; else logs no subscribers.Final Answer:
This code is correct and safely raises the event with logging -> Option AQuick Check:
Local copy + logging after invoke is best practice [OK]
- Logging before event may mislead if event throws
- Skipping local copy risks race conditions
- Ignoring null checks causes exceptions
Ready to prove it?
Pass the test and this pattern moves unknown
