Using lambda expressions for event handlers
There are no performance implications since the compiler will translate your lambda expression into an equivalent delegate. Lambda expressions are nothing more than a language feature that the compiler translates into the exact same code that you are used to working with.
The compiler will convert the code you have to something like this:
public partial class MyPage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
//snip
MyButton.Click += new EventHandler(delegate (Object o, EventArgs a)
{
//snip
});
}
}
lambda expression and event handler?
You can use a lambda expression to build an anonymous method, which can be attached to an event.
For example, if you make a Windows Form with a Button
and a Label
, you could add, in the constructor (after InitializeComponent()
):
this.button1.Click += (o,e) =>
{
this.label1.Text = "You clicked the button!";
};
This will cause the label to change as the button is clicked.
Passing definition of event handler as a lambda expression
FileSystemEventHandler
delegate has exactly two parameters - sender object
and FileSystemEventArgs
argument. And it doesn't return any value. I.e. it's signature looks like:
public void FileSystemEventHandler(object sender, FileSystemEventArgs e)
Lambda should match this signature - it should not return any values and it should accept two arguments as described above. You can use FileSystemEventHandler
or Action<object, FileSystemEventArgs>
delegate as method parameter:
public void watch(string pathName, FileSystemEventHandler OnChanged)
{
// ...
watcher.Created += OnChanged;
}
Passing lambda to this method:
w.watch(@"someLocation", (s,e) => MoveFiles.Move());
Note: there is no implicit conversion between FileSystemEventHandler
and Action<object, FileSystemEventArgs>
delegates. So if you'll use handler of Action<object, FileSystemEventArgs>
type, then you should attach it this way:
watcher.Created += new FileSystemEventHandler(OnChanged);
How to write lambda expression with EventHandler javafx
The lambda expression is here to replace the whole FunctionalInterface
and not only its method, so it's not constructor + lambda
but only lambda
:
Use the
EventHandler
as parameter :someNode.addEventHandler(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
System.out.println(event.hashCode());
}
});Becomes :
someNode.addEventHandler(MouseEvent.MOUSE_CLICKED,
event -> System.out.println(event.hashCode()));
Use the
EventHandler
in a variable :EventHandler<MouseEvent> eh = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
System.out.println(event.hashCode());
}
};It'll become :
EventHandler<MouseEvent> eh = e -> System.out.println(e.hashCode());
It's exists various way to use lambda, with or without parameter, like :
Runnable r = () -> System.out.println("Here");
Is it possible to target an EventHandler in a lambda expression?
No, it is not possible to target an event. Basically event is not a real type member, but just C# syntax which produces add_EventName and remove_EventName methods pair.
You could try refer to these internal methods name, but it's not possible in C# - http://msdn.microsoft.com/en-us/library/z47a7kdw.aspx
There are many similar questions in SO, with the same answer NO - like this one from Jon Skeet https://stackoverflow.com/a/4756021/2170171
If you're real crazy, you can try something like
private static void Subscribe(Action addHandler)
{
var IL = addHandler.Method.GetMethodBody().GetILAsByteArray();
// Magic here, in which we understand ClassName and EventName
???
}
with usage like
Subscribe(() => new Button().Click += null);
You could try using Cecil http://www.mono-project.com/Cecil for analyzing IL, or implement your own logic as it should not be too hard for predictable line of code.
I don't think that it is good solution though, as it just replaces one headache (proper event naming) with another one (proper Subscribe
calling). Though, it will help with rename stuff.
Best practices of using lambda expressions for event handlers
It's a perfectly reasonable idea - but in this particular case, I would use an anonymous method instead:
txtLogin.LostFocus += delegate
{
txtLogin.Text = "Login...";
txtLogin.ForeColor = SystemColors.InactiveCaptionText;
};
The benefit is that you don't have to specify the parameters - which makes it clearer that you're not using them. This is the only advantage that anonymous methods have over lambda expressions.
The performance hit is almost always going to be negligible. The inability to remove them afterwards is a very real problem if you do need to be able to remove the handler, but I find that often I don't. (Reactive Extensions has a nice approach to this - when you subscribe to an observable sequence, you're given back an IDisposable
which will remove the subscription if you call it. Very neat.)
How can one use a lambda to create a new EventHandler?
It turns out this is not so difficult, but does require some amount of code with a little bit of reflection.
The basic idea is to wrap the handler in a generic class parameterized by the event type
HandlerFor<T> : IDisposable where T : EventArgs.
This class then has a private member function matching the required event handler signature:
void Handle(object sender, T eventArgs)
that it will register on construction, unregister on disposal and that will invoke the given Action
supplied to it in its constructor whenever the event occurs.
To hide the implementation details, and expose only an IDisposable
as a handle for controlled event handler life cycle scope and unregistration, I wrapped this in an EventHandlerRegistry
class. This will also allow adding improvements in future if required, such as building a factory delegate instead of repeatedly calling Activator.CreateInstance()
to build the HandleFor
instances.
It looks like this:
public class EventHandlerRegistry : IDisposable
{
private ConcurrentDictionary<Type, List<IDisposable>> _registrations;
public EventHandlerRegistry()
{
_registrations = new ConcurrentDictionary<Type, List<IDisposable>>();
}
public void RegisterHandlerFor(object target, EventInfo evtInfo, Action eventHandler)
{
var evtType = evtInfo.EventHandlerType.GetGenericArguments()[0];
_registrations.AddOrUpdate(
evtType,
(t) => new List<IDisposable>() { ConstructHandler(target, evtType, evtInfo, eventHandler) },
(t, l) => { l.Add(ConstructHandler(target, evtType, evtInfo, eventHandler)); return l; });
}
public IDisposable CreateUnregisteredHandlerFor(object target, EventInfo evtInfo, Action eventHandler)
{
var evtType = evtInfo.EventHandlerType.GetGenericArguments()[0];
return ConstructHandler(target, evtType, evtInfo, eventHandler);
}
public void Dispose()
{
var regs = Interlocked.Exchange(ref _registrations, null);
if (regs != null)
{
foreach (var reg in regs.SelectMany(r => r.Value))
reg.Dispose();
}
}
private IDisposable ConstructHandler(object target, Type evtType, EventInfo evtInfo, Action eventHandler)
{
var handlerType = typeof(HandlerFor<>).MakeGenericType(evtType);
return Activator.CreateInstance(handlerType, target, evtInfo, eventHandler) as IDisposable;
}
private class HandlerFor<T> : IDisposable where T : EventArgs
{
private readonly Action _eventAction;
private readonly EventInfo _evtInfo;
private readonly object _target;
private EventHandler<T> _registeredHandler;
public HandlerFor(object target, EventInfo evtInfo, Action eventAction)
{
_eventAction = eventAction;
_evtInfo = evtInfo;
_target = target;
_registeredHandler = new EventHandler<T>(this.Handle);
_evtInfo.AddEventHandler(target, _registeredHandler);
}
public void Unregister()
{
var registered = Interlocked.Exchange(ref _registeredHandler, null);
if (registered != null)
// Unregistration is awkward:
// doing `RemoveEventHandler(_target, registered);` won't work.
_evtInfo.RemoveEventHandler(_target, new EventHandler<T>(this.Handle));
}
private void Handle(object sender, T EventArgs)
{
if (_eventAction != null)
_eventAction();
}
public void Dispose()
{
Unregister();
}
}
}
It supports clean adding and removing of event handlers in a pretty transparent manner. Note: this does not yet implement IDisposable
in the recommended manner. You will have to add finalizers and Dispose(bool isFinalizing)
yourself.
This shows an example of its usage:
public class MyArgs1 : EventArgs
{
public string Value1;
}
public class MyEventSource
{
public event EventHandler<MyArgs1> Args1Event;
public EventInfo GetEventInfo()
{
return this.GetType().GetEvent("Args1Event");
}
public void FireOne()
{
if (Args1Event != null)
Args1Event(this, new MyArgs1() { Value1 = "Bla " });
}
}
class Program
{
public static void Main(params string[] args)
{
var myEventSource = new MyEventSource();
using (var handlerRegistry = new EventHandlerRegistry())
{
handlerRegistry.RegisterHandlerFor(
myEventSource,
myEventSource.GetEventInfo(),
() => Console.WriteLine("Yo there's some kinda event goin on"));
handlerRegistry.RegisterHandlerFor(
myEventSource,
myEventSource.GetEventInfo(),
() => Console.WriteLine("Yeah dawg let's check it out"));
myEventSource.FireOne();
}
myEventSource.FireOne();
}
}
When run, it will give the below output:
How can lambda expressions as event handlers can change local variables?
Specifically, how would I write this without lambda expressions so that the local variable raised can be updated?
You would create an extra class, to hold the captured variables. That's what the C# compiler does. The extra class would contain a method with the body of the lambda expression, and the EventRaised
method would create an instance of that capturing class, using the variables within that instance instead of "real" local variables.
It's easiest to demonstrate this without using events - just a small console application. Here's the version with the lambda expression:
using System;
class Test
{
static void Main()
{
int x = 10;
Action increment = () => x++;
increment();
increment();
Console.WriteLine(x); // 12
}
}
And here's code which is similar to the code generated by the compiler:
using System;
class Test
{
private class CapturingClass
{
public int x;
public void Execute()
{
x++;
}
}
static void Main()
{
CapturingClass capture = new CapturingClass();
capture.x = 10;
Action increment = capture.Execute;
increment();
increment();
Console.WriteLine(capture.x); // 12
}
}
Of course it can get much more complicated than this, particularly if you have multiple captured variables with different scopes - but if you can understand how the above works, that's a big first step.
EventHandlers and Anonymous Delegates / Lambda Expressions
If object X has an event handler whose target is object Y, then object X being alive means that object Y can't be garbage collected. It doesn't stop object X from being garbage collected.
Normally when something is disposed, it will become garbage pretty soon anyway, which means you don't have a problem.
The problem with events and GC is if you forget to remove a subscribed handler from a different object - i.e. you have a listener which is disposed, but will never be garbage collected because there's still a reference to it from the event in a different object.
Related Topics
Add Two Lists of Different Length in C#
3D Relative Angle Sum Calculation
Practical Applications of Bitwise Operations
What Do Programmers Mean When They Say, "Code Against an Interface, Not an Object."
How to Route Images Using ASP.NET MVC Routing
C# Generic "Where Constraint" with "Any Generic Type" Definition
Button Inside a Winforms Textbox
Entity Framework - Retrieve Id Before 'Savechanges' Inside a Transaction
C# Guid and SQL Uniqueidentifier
System.Web.Httpcontext.Current.User.Identity.Name VS System.Environment.Username in ASP.NET
Creating Diagonal Pattern in Wpf
Wrap C# Application in .Msi Installer
Spawn Multiple Threads for Work Then Wait Until All Finished
Observablecollection and Item Propertychanged