Unsubscribing from an anonymous event handler inside a static method (extension method)

I have an extension method for subscribing to the PropertyChanged event of an object that implements INotifyPropertyChanged .

I would like the event to fire only once. Not more.

This is my method.

 public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action) { if (target == null) { return; } PropertyChangedEventHandler handler = (obj, e) => { if (propertyName == e.PropertyName) { action(); } }; target.PropertyChanged -= handler; target.PropertyChanged += handler; } 

But that will not work. I cannot remove the event handler so that the event fires every time I call this method.

I tried a different approach. Instead of using anonymous methods, something more traditional, for example:

 public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action) { if (target == null) { return; } target.PropertyChanged -= target_PropertyChanged; target.PropertyChanged += target_PropertyChanged; } static void target_PropertyChanged(object sender, PropertyChangedEventArgs e) { //do stuff here } 

And it just works great. The event fires only once, but I also need the Action parameter. I can not use it with this approach.

Any workaround or other aproach to solve this problem? Is there anything strange with anonymous methods inside static methods?

Thanks in advance.

+7
source share
3 answers

This limits the use of anonymous methods as event handlers. They cannot be deleted in the same way as a regular method (which is automatically created by a delegate instance by converting a group of methods), since anonymous methods are compiled into the container class created by each instance, and a new instance of the class is created each time.

To save the action parameter, you can create a container class that will have a delegate for your event handler inside. A class can be declared private in another class that you are working with, or made internal, possibly in the Helpers namespace. It will look something like this:

 class DelegateContainer { public DelegateContainer(Action theAction, string propName) { TheAction = theAction; PopertyName = propName; } public Action TheAction { get; private set; } public string PropertyName { get; private set; } public void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) { if(PropertyName == e.PropertyName) TheAction(); } } 

Then create and save the container reference in your class. You can create a static member currentContainer , and then set the handler as follows:

 private static DelegateContainer currentContainer; public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action) { if (target == null) { return; } if(currentContainer != null) target.PropertyChanged -= currentContainer.PropertyChangedHandler; currentContainer = new DelegateContainer(action, propertyName); target.PropertyChanged += currentContainer.PropertyChangedHandler; } 
+3
source

You can run your first example if you cancel your subscription in the event handler itself.

 public static void OnPropertyChanged<T>(this INotifyPropertyChanged target, string propertyName, Action action) { if (target == null) { return; } // Declare the handler first, in order to create // a concrete reference that you can use from within // the delegate PropertyChangedEventHandler handler = null; handler = (obj, e) => { if (propertyName == e.PropertyName) { obj.PropertyChanged -= handler; //un-register yourself action(); } }; target.PropertyChanged += handler; } 

The above code serves as the same event handler. You can register an unlimited number of them, and each of them will be executed only once before the registration is canceled.

Keep in mind that one of these handlers can execute several times if you quickly increase the event on multiple threads. To prevent this from happening, you may need to create a static Dictionary (T, T) display of object objects to “block objects” and add some time code so that the handler runs only once. However, these implementation features are a bit outside of your question, as currently written.

+3
source

Technically, this is not the same anonymous method that you are trying to cancel. OnPropertyChanged creates a new instance of this method each time OnPropertyChanged called. Therefore, unsubscribing will not work.

0
source

All Articles