How to Remove a Lambda Event Handler

How to remove a lambda event handler

The C# specification explicitly states (IIRC) that if you have two anonymous functions (anonymous methods or lambda expressions) it may or may not create equal delegates from that code. (Two delegates are equal if they have equal targets and refer to the same methods.)

To be sure, you'd need to remember the delegate instance you used:

EventHandler handler = (s, e) => MessageBox.Show("Woho");

button.Click += handler;
...
button.Click -= handler;

(I can't find the relevant bit of the spec, but I'd be quite surprised to see the C# compiler aggressively try to create equal delegates. It would certainly be unwise to rely on it.)

If you don't want to do that, you'll need to extract a method:

public void ShowWoho(object sender, EventArgs e)
{
MessageBox.Show("Woho");
}

...

button.Click += ShowWoho;
...
button.Click -= ShowWoho;

If you want to create an event handler which removes itself using a lambda expression, it's slightly trickier - you need to refer to the delegate within the lambda expression itself, and you can't do that with a simple "declare a local variable and assign to it using a lambda expression" because then the variable isn't definitely assigned. You typically get around this by assigning a null value to the variable first:

EventHandler handler = null;
handler = (sender, args) =>
{
button.Click -= handler; // Unsubscribe
// Add your one-time-only code here
}
button.Click += handler;

Unfortunately it's not even easy to encapsulate this into a method, because events aren't cleanly represented. The closest you could come would be something like:

button.Click += Delegates.AutoUnsubscribe<EventHandler>((sender, args) =>
{
// One-time code here
}, handler => button.Click -= handler);

Even that would be tricky to implement within Delegates.AutoUnsubscribe because you'd have to create a new EventHandler (which would be just a generic type argument). Doable, but messy.

Remove eventHandler that was added using lambda expression

The problem that it's not getting removed is because you're giving a new lambda expression when removing it. You need to keep the reference of delegate created by Lambda expression to remove it from the control.

EventHandler handler = (x, y) => comboBox1_DropDown(x, y);
comboBox1.DropDown += handler;

It will work simply like:

comboBox1.DropDown -= handler;

via reflection:

    private void RemoveEvent(ComboBox b, EventHandler handler)
{
EventInfo f1 = typeof(ComboBox).GetEvent("DropDown");
f1.RemoveEventHandler(b, handler);
}

Adding and Removing Anonymous Event Handler

There's an MSDN page that talks about this:

How to Subscribe to and Unsubscribe from Events

Note in particular:

If you will not have to unsubscribe to [sic]
an event later, you can use the
addition assignment operator (+=) to
attach an anonymous method to the
event.

And also:

It is important to notice that you
cannot easily unsubscribe from an
event if you used an anonymous
function to subscribe to it. To
unsubscribe in this scenario, it is
necessary to go back to the code where
you subscribe to the event, store the
anonymous method in a delegate
variable, and then add the delegate to
the event . In general, we recommend
that you do not use anonymous
functions to subscribe to events if
you will have to unsubscribe from
the event at some later point in your
code.


JavaFX remove eventhandler from lambda expression

If you use an anonymous class instead of a lambda you can reference the EventHandler with this from inside the handle method:

EventHandler<MouseEvent> canvasHandler = new EventHandler<>() {
@Override public void handle(MouseEvent event) {
// handle event...
if (/* condition */) {
canvas.removeEventHandler(MouseEvent.MOUSE_CLICKED, this);
});
canvas.addEventHandler(MouseEvent.MOUSE_CLICKED, canvasHandler);

remove lambda expression from PropertyChanged (equivalent of p.PropertyChanged -= (s,a) = { ... })

You must put it in a variable:

PropertyChangedEventHandler eventHandler = (s, a) => {
...
};

// ...
// subscribe
p.PropertyChanged += eventHandler;
// unsubscribe
p.PropertyChanged -= eventHandler;

From the docs:

It is important to notice that you cannot easily unsubscribe from an event if you used an anonymous function to subscribe to it. To unsubscribe in this scenario, it is necessary to go back to the code where you subscribe to the event, store the anonymous method in a delegate variable, and then add the delegate to the event. In general, we recommend that you do not use anonymous functions to subscribe to events if you will have to unsubscribe from the event at some later point in your code.


Unsubscribe Lambda Event Handler **With Closure**

No, you have to store the references to the delegates, basically.

Remember everything that you'll want to unsubscribe later.

How to unsubscribe from an event which uses a lambda expression?

If you need to unsubscribe from an event, you need an instanced reference. Unfortunately, that means you can't use that particular syntax.

How to remove yourself from an event handler?

There is no simple method to achieve this.

Preferred approach:

I don't see why you can't save the delegate. You don't have to save the instance as some field. It can be a local variable that is captured by your anonymous event handler:

EventHandler<TypeOfEventArgs> handler = null;
handler = (s, e) =>
{
// Do whatever you need to do here

// Remove event:
foo.Event -= handler;
}

foo.Event += handler;

I can't think of a single scenario where you can't use this.

Alternative approach without saving the delegate:

However, if you have such a scenario, it get's quite tricky.

You need to find the delegate that has been added as a handler to the event. Because you didn't save it, it is pretty hard to obtain it. There is no this to get a delegate of the currently executing method.

You can't use GetInvocationList() on the event either, because accessing an event outside the class it is defined in is restricted to adding and removing handlers, i.e. += and -=.

Creating a new delegate isn't possible either. While you can get access to the MethodInfo object defining your anonymous method, you can't get access to the instance of the class that method is declared in. This class is generated automatically by the compiler and calling this inside the anonymous method will return the instance of the class your normal method is defined in.

The only way I found that works is to find the field - if any - that the event uses and call GetInvocationList() on it. The following code demonstrates this with a dummy class:

void Main()
{
var foo = new Foo();
foo.Bar += (s, e) => {
Console.WriteLine("Executed");

var self = new StackFrame().GetMethod();
var eventField = foo.GetType()
.GetField("Bar", BindingFlags.NonPublic |
BindingFlags.Instance);
if(eventField == null)
return;
var eventValue = eventField.GetValue(foo) as EventHandler;
if(eventValue == null)
return;
var eventHandler = eventValue.GetInvocationList()
.OfType<EventHandler>()
.FirstOrDefault(x => x.Method == self)
as EventHandler;
if(eventHandler != null)
foo.Bar -= eventHandler;
};

foo.RaiseBar();
foo.RaiseBar();
}

public class Foo
{
public event EventHandler Bar;
public void RaiseBar()
{
var handler = Bar;
if(handler != null)
handler(this, EventArgs.Empty);
}
}

Please note that the string "Bar" that is passed to GetField needs to be the exact name of the field that is used by the event. This results in two problems:

  1. The field can be named differently, e.g. when using an explicit event implementation. You need to manually find out the field name.
  2. There might be no field at all. This happens if the event uses an explicit event implementation and just delegates to another event or stores the delegates in some other way.

Conclusion:

The alternative approach relies on implementation details, so don't use it if you can avoid it.

removeEventListener on anonymous functions in JavaScript

I believe that is the point of an anonymous function, it lacks a name or a way to reference it.

If I were you I would just create a named function, or put it in a variable so you have a reference to it.

var t = {};
var handler = function(e) {
t.scroll = function(x, y) {
window.scrollBy(x, y);
};
t.scrollTo = function(x, y) {
window.scrollTo(x, y);
};
};
window.document.addEventListener("keydown", handler);

You can then remove it by

window.document.removeEventListener("keydown", handler);   


Related Topics



Leave a reply



Submit