One of the niceties included with C# 3.0 is lambda expressions, a concise syntax for implementing anonymous methods. Lambda expressions can be over-used, but they're very handy for certain kinds of things, like defining how asynchronous callbacks should be handled, all within a single method.
Lambda expressions are also very handy for implementing event handlers. For instance, if you've got an AlarmClock class that looks like this:
class AlarmClock { public event EventHandler<EventArgs> Alarm; public void SoundAlarm() { if (Alarm != null) { Alarm(this, new EventArgs()); } } }
You would normally assign and implement an event-handler like this, with a separate defined method:
class Program { public static void Main() { AlarmClock clock = new AlarmClock(); clock.Alarm += new EventHandler<EventArgs>(clock_Alarm); clock.SoundAlarm(); } static void clock_Alarm(object sender, EventArgs e) { Console.WriteLine("The alarm went off at {0}.", DateTime.Now); } }
But with lambda expressions, you can do it like this, all in one method:
class Program { public static void Main() { AlarmClock clock = new AlarmClock(); clock.Alarm += (s, e) => Console.WriteLine("The alarm went off at {0}.", DateTime.Now); clock.SoundAlarm(); } }
Most folks would agree that this looks cleaner, and is easier to follow. (At least, once you get the hang of lambda expressions, which admittedly have a rather odd syntax.)
The one time this doesn't work is when you need to be able to clear event handlers as well as assign them. The way that you normally do this is so:
public static void Main() { AlarmClock clock = new AlarmClock(); clock.Alarm += new EventHandler<EventArgs>(clock_Alarm); clock.SoundAlarm(); clock.Alarm -= new EventHandler<EventArgs>(clock_Alarm); }
But you can't do that with the lambda expression I used above, because you don't have any way to identify the lambda expression that you want to remove. I've seen a variety of folks asking questions about how to clear event handlers implemented as lambda expressions for this very reason.
It turns out that there are (at least) two ways to do it.
If you've got access to the source code for the class in question, you can write a public ClearEventHandlers() method that works like so:
class AlarmClock { public event EventHandler<EventArgs> Alarm; public void SoundAlarm() { if (Alarm != null) { Alarm(this, new EventArgs()); } } public void ClearEventHandlers() { Alarm = null; } }
But if you need more granularity, or don't have access to the source, you can assign the lambda expression to a variable (e.g., "handleAlarm") before you assign it as an event-handler, like so:
public static void Main() { AlarmClock clock = new AlarmClock(); EventHandler<EventArgs> handleAlarm = null; handleAlarm = (s, e) => { clock.Alarm -= handleAlarm; Console.WriteLine("The alarm went off at {0}.", DateTime.Now); }; clock.Alarm += handleAlarm; clock.SoundAlarm(); }
Note that you need to assign the handleAlarm variable to null first, because otherwise the compiler complains about the first line of the lambda expression: it thinks that you're trying to muck about with an uninitialized variable. This is only sort of true, but to work around it, just assign the variable to null when you declare it.
The syntax for #2 is a bit unwieldy, but it can let you do things that would be difficult or complicated if you had to use a second method. For instance, within lambda expressions you can use local variables declared at the level of the containing function:
public static void Main() { AlarmClock clock = new AlarmClock(); EventHandler<EventArgs> handleAlarm = null; ManualResetEvent resetEvent = new ManualResetEvent(false); bool alarmSounded = false; handleAlarm = (s, e) => { clock.Alarm -= handleAlarm; Console.WriteLine("The alarm went off at {0}.", DateTime.Now); alarmSounded = true; resetEvent.Set(); }; clock.Alarm += handleAlarm; clock.SoundAlarm(); resetEvent.WaitOne(5000); if (!alarmSounded) { Console.WriteLine("The alarm didn't sound within the timeout period."); } }
To duplicate this without lambda expressions, you'd have to declare the resetEvent and alarmSounded variables at the class level, which isn't nearly as clean, and could lead to some odd bugs if other methods were trying to use those same variables simultaneously.