C#
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C#
-
Section 4: Working with Data
-
Section 5: Error Handling
-
Section 6: Delegates and Events
-
Section 7: Async Programming
-
Section 8: More Language Features
-
Section 9: File I/O and Serialization
-
Section 10: Networking in .NET
-
Section 11: The .NET Ecosystem
-
Section 12: Memory and Performance
-
Section 13: Concurrency Beyond Async
-
Section 14: Reflection and Attributes
-
Section 15: Testing and Best Practices
-
Section 16: Design Patterns in C#
-
Section 17: Standard Library Deep Dive
-
Section 18: Data Structures and Algorithms in C#
-
Section 19: GUI and Desktop Development Overview
-
Section 20: Practical Projects
-
Section 21: More Practice Exercises
-
Section 22: More Standard Library and Text Processing
-
Section 23: More Design Patterns
-
Section 24: More Projects
-
Section 25: Interview Practice
-
Section 26: C# Keywords Reference (Modifiers)
-
Section 27: C# Keywords Reference (Statements)
-
Section 28: C# Keywords Reference (Operators)
-
Section 29: BCL: System.Collections.Generic
-
Section 30: BCL: System.Linq
-
Section 31: BCL: System.Threading
-
Section 32: BCL: System.IO
-
Section 33: BCL: System.Text and System.Text.Json
-
Section 34: BCL: System.Net.Http
-
Section 35: C# Language Specification Topics
-
Section 36: More Practice Exercises
-
Section 37: More Async Patterns
-
Section 38: More BCL: System.Reflection and System.Diagnostics
-
Section 39: Nullable Reference Types In Depth
-
Section 40: C# Records and Pattern Matching In Depth
-
Section 41: Dependency Injection Deep Dive
-
Section 42: More Interview and Whiteboard Practice
250: Common C# Interview Questions on Delegates vs Events
I've seen this happen more times than I can count in code reviews. A developer wants to implement a notification system, so they use a public delegate. It seems to work perfectly during the first five minutes of testing, but then it creates a nightmare for maintainability and stability. Let's look at a realistic example of where this goes sideways.
public class TemperatureSensor
{
// A public delegate to notify listeners when the temp changes
public Action<float> OnTemperatureChanged;
public void UpdateTemperature(float newTemp)
{
Console.WriteLine($"Sensor reading: {newTemp}°C");
OnTemperatureChanged?.Invoke(newTemp);
}
}
public class Program
{
public static void Main()
{
var sensor = new TemperatureSensor();
// Subscriber 1: The Logger
sensor.OnTemperatureChanged += (t) => Console.WriteLine($"Log: Temp is {t}");
// Subscriber 2: The Alarm (but they made a mistake)
// Instead of +=, they used =
sensor.OnTemperatureChanged = (t) => {
if (t > 100) Console.WriteLine("ALARM! OVERHEATING!");
};
sensor.UpdateTemperature(110);
// Wait... where did the Logger go?
}
}
The Accidental Wipeout and the Security Hole
If you run that code, you'll notice the "Log: Temp is 110" message completely disappeared. Because OnTemperatureChanged is just a public delegate field, any piece of code anywhere in the application can use the assignment operator (=) to wipe out every other subscriber. In a large system, debugging why a specific listener stopped firing is a total waste of your afternoon.
But there's a worse problem here: encapsulation. Because that delegate is public, any class can call sensor.OnTemperatureChanged.Invoke(999). Imagine if a UI component accidentally "triggered" the sensor's notification logic, tricking the rest of the system into thinking the temperature just spiked, even though the hardware sensor never actually read that value. The class has lost control over its own internal state.
Enforcing Encapsulation with the Event Keyword
To fix this, we use the event keyword. Think of an event as a protective wrapper around a delegate. It restricts the "outside world" to only two operations: subscribing (+=) and unsubscribing (-=). It prevents anyone from overwriting the list or triggering the event from outside the class.
public class TemperatureSensor
{
// Adding the 'event' keyword changes the access rules
public event Action<float> OnTemperatureChanged;
public void UpdateTemperature(float newTemp)
{
Console.WriteLine($"Sensor reading: {newTemp}°C");
// This is still allowed because we are inside the TemperatureSensor class
OnTemperatureChanged?.Invoke(newTemp);
}
}
public class Program
{
public static void Main()
{
var sensor = new TemperatureSensor();
sensor.OnTemperatureChanged += (t) => Console.WriteLine($"Log: Temp is {t}");
// THIS WILL NOW CAUSE A COMPILE-TIME ERROR:
// sensor.OnTemperatureChanged = (t) => { ... };
// THIS WILL ALSO CAUSE A COMPILE-TIME ERROR:
// sensor.OnTemperatureChanged.Invoke(100);
}
}
Now, the compiler protects us. The event keyword ensures that the TemperatureSensor is the only entity that can decide when the notification happens, and it ensures that subscribers can't accidentally kick each other off the list.
Winning the "Delegates vs Events" Interview Question
When an interviewer asks you the difference between a delegate and an event, they aren't just checking if you know the syntax. They are checking if you understand encapsulation. If you just say "events use delegates," you're giving a textbook answer that doesn't impress anyone.
I recommend framing your answer around these three specific points:
- Ownership: A delegate is a type; an event is a modifier. An event is essentially a delegate that is encapsulated so that only the declaring class can invoke it.
- Safety: Events prevent external classes from using the assignment operator (
=), which would otherwise overwrite all existing subscriptions. - Intent: Using an event signals to other developers that "this is a notification you can listen to, but you are not allowed to trigger it yourself."
📋 Practical Task
Fixing the Broken Stock Ticker Notification System
You have been handed a StockTicker class that is causing intermittent bugs in a trading app. Currently, it uses a public delegate, and a junior developer has accidentally written code that triggers fake price updates from the UI layer, causing the trading bot to make bad trades.
Your Task:
- Modify the
StockTickerclass to use aneventinstead of a public delegate to ensure that only theStockTickercan trigger notifications. - Ensure that the
UpdatePricemethod still triggers the notification. - In the
Mainmethod, attempt to callticker.OnPriceChanged.Invoke(...)and verify that the code no longer compiles (you can comment this line out after verifying the error).
using System;
public class StockTicker
{
// TODO: Change this from a public delegate to an event
public Action<string, decimal> OnPriceChanged;
public void UpdatePrice(string symbol, decimal price)
{
Console.WriteLine($"Updating {symbol} to {price}...");
OnPriceChanged?.Invoke(symbol, price);
}
}
public class Program
{
public static void Main()
{
var ticker = new StockTicker();
ticker.OnPriceChanged += (symbol, price) =>
Console.WriteLine($"Notification: {symbol} is now {price}");
ticker.UpdatePrice("MSFT", 420.50m);
// TODO: This line should be blocked by the compiler after your changes
// ticker.OnPriceChanged.Invoke("MSFT", 1000.00m);
}
}There are no comments for now.