Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove yourself from an event handler?

What I want to do is basically remove a function from an event, without knowing the function's name.

I have a FileSystemWatcher. If a file is created/renamed it checks its name. If it matches, it then moves it to a specific location. However, if the file is locked, it makes a lambda that attaches to a timer's tick event, waiting until the file is not locked. When it isn't, it moves the file and then removes itself from the event handler. I've seen lots of ways to do this, like keeping the instance, or making a named method. I can't do either of those here. What are my options?

like image 853
It'sNotALie. Avatar asked May 24 '13 12:05

It'sNotALie.


People also ask

Which of the following method can be used to remove event handlers?

Answer: Use the jQuery off() Method You can simply use the jQuery off() method to remove the event handlers that were attached with on() .

How do I get rid of anonymous event handler?

Strictly speaking you can't remove an anonymous event listener unless you store a reference to the function. Since the goal of using an anonymous function is presumably not to create a new variable, you could instead store the reference in the element itself: element. addEventListener('click',element.

How do I turn off event listener?

The removeEventListener() is an inbuilt function in JavaScript which removes an event handler from an element for a attached event. for example, if a button is disabled after one click you can use removeEventListener() to remove a click event listener.


1 Answers

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.

like image 145
Daniel Hilgarth Avatar answered Oct 20 '22 05:10

Daniel Hilgarth