Subscribing to an event with a method that requires state is a pain because it often requires a class to be created to hold that state. Fortunately we can use a lambda expression to have the compiler create that state for us instead.
It is not an uncommon scenario to have an event handler that will do something with some specific data when an event fires. An example I had lately was a mail helper that only would send the email after the Entity Framework DbContext successfully has executed SaveChanges()
. That way I knew that the database was properly updated with the state that the mail was indeed sent. Everything was wrapped in a transaction too, so if the mail failed to send the entire transaction was rolled back, undoing the database changes.
Anyway, that code could be a pain to write. I would have to create a separate object that held a reference to the e-mail to send and subscribe a method of that class to the DbContext’s SavingChanges()
event (ok, it’s a bit more complicated than that to get to the SavingChanges
event, but that’s another topic). Instead I used a lambda expression that creates a closure that automatically captures the required variable.
For a really simple example, I’ll show a helper function that schedules an asynchronous push of a specific number to a Stack
after a timeout. The code is deliberately not thread safe regarding the Stack
to not clutter up the use of the lambda.
public static void Run(Stack<int> result) { ScheduleAdd(result, number: 1, timeoutms: 2); ScheduleAdd(result, number: 2, timeoutms: 1); System.Threading.Thread.Sleep(20); } private static void ScheduleAdd(Stack<int> stack, int number, int timeoutms) { var t = new Timer(timeoutms) { AutoReset = false }; t.Elapsed += (object sender, ElapsedEventArgs eventArgs) => stack.Push(number); t.Enabled = true; } |
The event handler has to know what number to push. Using the lambda, variable capturing handles it. At a glance, this doesn’t look complicated but it is. The variable number
is used inside the event handler. That variable number
is an argument to the ScheduleAdd
function. When the event fires and the event handler runs, it’s a long time (at least for a computer) since the ScheduleAdd
function returned so the number
variable has gone out of scope and is gone. How does the event handler know what number to push? Even more complicated, we’re scheduling two different adds, so each instance of the event handler has to keep track of its own number.
The answer is that the compiler creates a small helper class for us, instantiating an object of that class for each time the lambda is used. After the compiler has done its magic there is indeed a small helper object that is used to subscribe to the event. The good thing is that the compiler writes it automatically, meaning less code to maintain.
Variable capturing in closures is a bit of magic, but it is really powerful magic that can be utilized in many ways to write less code that’s on the same time more readable.