How to remove itself from an event handler?

What I want to do is basically remove the function from the event without knowing the function name.

I have a FileSystemWatcher . If the file is created / renamed, it checks its name. If it matches, it moves it to a specific location. However, if the file is locked, it binds the lambda to the timer event, waiting for the file to be locked. When this is not the case, it moves the file and then is removed from the event handler. I have seen many ways to do this, such as saving an instance or creating a named method. I can do neither one nor the other. What are my options?

+8
c # lambda events timer
source share
2 answers

There is no simple method to achieve this.

Preferred Approach:

I do not understand why you cannot save the delegate. You do not need to save the instance as some field. It could be a local variable 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 cannot imagine a single scenario where you cannot use this.

Alternative approach without saving the delegate:

However, if you have such a scenario, it becomes quite difficult.
You need to find the delegate that was added as the event handler. Since you did not save it, it is quite difficult to get it. There is no this to get the delegate of the current executable method.

You cannot use GetInvocationList() in an event, since access to the event outside the class in which it is defined is limited to adding and removing handlers, i.e. += and -= .

Creating a new delegate is also not possible. Although you can access the MethodInfo object that defines your anonymous method, you cannot access the instance of the class in which this method is declared. This class is automatically generated by the compiler and calling this inside the anonymous method will return an instance of the class in which your regular method is defined.

The only way I found work is to find the field - if there is one - this event uses and calls GetInvocationList() . 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); } } 

Note that the string "Bar" that is passed to GetField must be the exact name of the field used by the event. This leads to two problems:

  • A field can be named differently, for example. when using an explicit implementation of the event. You need to manually find out the name of the field.
  • There can be no field. This happens if the event uses an explicit implementation of the event and simply delegates to another event or saves the delegates in some other way.

Output:

An alternative approach depends on implementation details, so do not use it if you can avoid it.

+20
source share

Steps to remove an event handler using a lambda expression:

 public partial class Form1 : Form { private dynamic myEventHandler; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { myEventHandler = new System.EventHandler((sender2, e2) => this.button1_Click(sender, e, "Hi there")); this.button1.Click += myEventHandler; } private void button1_Click(object sender, EventArgs e, string additionalInfo) { MessageBox.Show(additionalInfo); button1.Click -= myEventHandler; } } 
0
source share

All Articles