Using Lambda Expressions for Event Handlers

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 :

  1. 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()));


  1. 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:

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



Leave a reply



Submit