Delegates, Events & Lambdas in C# – A Practical Guide
1. What Are Delegates?
Delegates in C# are like function pointers in C++, but type-safe. A delegate is a reference type that holds the reference to a method. It can invoke any method that matches its signature. Delegates are the backbone of event handling and LINQ operations in C#.
1.1 Basic Delegate Declaration and Use
public delegate void DisplayMessage(string message);
class Program
{
static void Show(string msg)
{
Console.WriteLine("Message: " + msg);
}
static void Main()
{
DisplayMessage del = Show;
del("Hello, Delegates!");
}
}
2. Multicast Delegates
A delegate can point to multiple methods. When invoked, all the methods are called in the order they were added.
public delegate void MultiDelegate();
class Program
{
static void MethodA() => Console.WriteLine("Method A");
static void MethodB() => Console.WriteLine("Method B");
static void Main()
{
MultiDelegate del = MethodA;
del += MethodB;
del(); // Calls both MethodA and MethodB
}
}
3. What Are Events in C#?
Events are a way to provide notifications. An event is a wrapper around a delegate. Only the class that defines the event can invoke it, but other classes can subscribe to it.
public class Alarm
{
public event Action OnRing;
public void Ring()
{
if (OnRing != null)
{
OnRing();
}
}
}
class Program
{
static void Alert() => Console.WriteLine("Alarm ringing!");
static void Main()
{
Alarm alarm = new Alarm();
alarm.OnRing += Alert;
alarm.Ring();
}
}
4. Lambda Expressions
Lambdas are anonymous functions. They provide a short and expressive way to write methods directly inline.
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(10, 5)); // Output: 15
5. Built-in Delegates: Action, Func, and Predicate
5.1 Action Delegate
Action represents a method that returns void. You can use it when you just want to perform an action without returning anything.
Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
greet("Alice");
5.2 Func Delegate
Func returns a value. The last type parameter represents the return type.
Func<int, int, int> multiply = (x, y) => x * y;
Console.WriteLine(multiply(4, 5)); // Output: 20
5.3 Predicate Delegate
Predicate always returns a boolean value and takes one input parameter.
Predicate<int> isEven = num => num % 2 == 0;
Console.WriteLine(isEven(10)); // Output: True
6. Combining Delegates with Lambdas
Lambdas work beautifully with delegates, especially when you need to define behavior inline without creating separate method definitions. This is common in event handling and LINQ queries.
Action show = () => Console.WriteLine("This is a lambda with Action!");
show();
Or use lambdas in combination with your own delegates:
public delegate int Operation(int x, int y);
class Program
{
static void Main()
{
Operation add = (a, b) => a + b;
Operation sub = (a, b) => a - b;
Console.WriteLine(add(10, 5)); // Output: 15
Console.WriteLine(sub(10, 5)); // Output: 5
}
}
7. Events with Custom EventArgs
For more informative event handling, you can define custom EventArgs
to pass additional data when an event is raised.
public class TemperatureEventArgs : EventArgs
{
public int CurrentTemp { get; set; }
}
public class Thermostat
{
public event EventHandler<TemperatureEventArgs> TemperatureChanged;
public void ChangeTemperature(int newTemp)
{
TemperatureChanged?.Invoke(this, new TemperatureEventArgs { CurrentTemp = newTemp });
}
}
class Program
{
static void Main()
{
Thermostat t = new Thermostat();
t.TemperatureChanged += (sender, e) =>
{
Console.WriteLine("New Temperature: " + e.CurrentTemp);
};
t.ChangeTemperature(75);
}
}
8. Real-World Example: Event-Driven Design
Let’s say you're building a file downloader app. You might want to notify the UI when the download completes. That’s where events and delegates come in.
public class FileDownloader
{
public event Action DownloadComplete;
public void StartDownload()
{
Console.WriteLine("Downloading...");
Thread.Sleep(2000); // Simulate download
DownloadComplete?.Invoke();
}
}
class Program
{
static void Main()
{
FileDownloader downloader = new FileDownloader();
downloader.DownloadComplete += () => Console.WriteLine("Download finished!");
downloader.StartDownload();
}
}
This event-driven model makes your applications more modular, scalable, and responsive.
9. LINQ with Delegates and Lambdas
Lambdas and built-in delegates like Func
and Predicate
are central to LINQ in C#. Here’s an example of using them together in a LINQ query:
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
This filters out only even numbers using a lambda passed to the Where
method, which accepts a Func<int, bool>
delegate.
10. Conclusion
Delegates, events, and lambdas are foundational to modern C# development. They offer a powerful way to decouple code, implement callbacks, handle events, and process data efficiently with LINQ. Whether you’re wiring up UI events or building dynamic business logic, understanding how to work with these features makes your code more flexible, maintainable, and expressive.
FAQs
1. Can I pass methods as parameters in C#?
Yes, using delegates like Action
, Func
, or custom delegate types, you can pass methods around as parameters.
2. What’s the difference between a delegate and an event?
A delegate is a function pointer. An event is a wrapper around a delegate that restricts direct invocation from outside the declaring class.
3. When should I use Func over Action?
Use Func
when your delegate needs to return a value; use Action
when it returns void.
4. What is the difference between lambda expressions and anonymous methods?
Both are anonymous, but lambdas use the =>
syntax and are more concise. They also support expression trees and LINQ better.
5. How do I unsubscribe from an event in C#?
You can unsubscribe by using the -= operator with the same method reference: eventName -= handlerMethod;
Post a Comment