How to Correctly Unregister an Event Handler

How to correctly unregister an event handler

The C# compiler's default implementation of adding an event handler calls Delegate.Combine, while removing an event handler calls Delegate.Remove:

Fire = (MyDelegate) Delegate.Remove(Fire, new MyDelegate(Program.OnFire));

The Framework's implementation of Delegate.Remove doesn't look at the MyDelegate object itself, but at the method the delegate refers to (Program.OnFire). Thus, it's perfectly safe to create a new MyDelegate object when unsubscribing an existing event handler. Because of this, the C# compiler allows you to use a shorthand syntax (that generates exactly the same code behind the scenes) when adding/removing event handlers: you can omit the new MyDelegate part:

Fire += OnFire;
Fire -= OnFire;

When the last delegate is removed from the event handler, Delegate.Remove returns null. As you have found out, it's essential to check the event against null before raising it:

MyDelegate handler = Fire;
if (handler != null)
handler("Hello 3");

It's assigned to a temporary local variable to defend against a possible race condition with unsubscribing event handlers on other threads. (See my blog post for details on the thread safety of assigning the event handler to a local variable.) Another way to defend against this problem is to create an empty delegate that is always subscribed; while this uses a little more memory, the event handler can never be null (and the code can be simpler):

public static event MyDelegate Fire = delegate { };

unregistering event handler

The reason that your code is broken is because you created these two methods:

public void SubscribeToMessages(Action<object, EventArgs> eventHandler)
{
this.MessageReceived += new EventHandler<EventArgs>(eventHandler);
}

public void UnsubscribeFromMessages(Action<object, EventArgs> eventHandler)
{
this.MessageReceived -= new EventHandler<EventArgs>(eventHandler);
}

instead of just adding/removing handlers from the event (which is what you should do) you're actually doing something quite different.

SubscribeToMessages is adding an event handler which, when invoked, will call the Invoke method of the eventHandler delegate.

When you call UnsubscribeFromMessages you're trying to remove the handler who's body is a call to the Invoke method of the eventHandler instance you've been passed. However, you're passing in different Action instances to each of these two method calls (even though those two different actions both point to the same method/object pair), so the event handlers you're trying to add/remove are each referring to different Action instances, and thus aren't considered equal.

If you just add/remove handlers to the event directly, instead of adding a second layer of indirection where you add an event handler that invokes an event handler that invokes the actual method, you'll be fine.

Alternatively, don't have the subscribe/unsubscribe methods that you are using have a different delegate than your event. Have them accept delegates of the type of the actual event, so that you can simply add/remove them, which will also remove the extra layer of indirection.

When to remove event handlers from an object?

  1. Yes that will cause another subscription which causes the handler to execute twice. You can remove the loaded handler inside the loaded handler.

    MSDN:

    Loaded and Unloaded might both be raised on controls as a result of user-initiated system theme changes. A theme change causes an invalidation of the control template and the contained visual tree, which in turn causes the entire control to unload and reload. Therefore Loaded cannot be assumed to occur only when a page is first loaded through navigation to the page.

  2. If the object is gone it cannot raise any events, so no need to do anything about that. And the handler will not keep the object alive (it's the other way around).

How do I Unregister 'anonymous' event handler

If you need to unregister an event, I recommend avoiding anonymous delegates for the event handler.

This is one case where assigning this to a local method is better - you can unsubscribe from the event cleanly.

What is the correct way to Unhook event Handlers in a ViewModel

I usually unregister event handlers on my view-model during navigation.

For example, when the OnNavigatedFrom event is raised (on your view), you can unregister the event handlers on your current view-model. Then when the OnNavigatedTo event is raised, you can re-register the event handlers.

In regards to IDisposable, I am not sure.
I thought IDisposable was for managing resources and not business logic.

How to unregister and register an awaitable Event handler?

Which syntax do I need to use to add and remove an async event
handler?

The same syntax as you'd need to use with regular event handlers. You need to save the delegate somewhere so you can later de-register it:

private EventHandler eventHandler = 
new EventHandler(async (s, e) => await FooAsync(s, e));

public async void SomeOtherEventHandler()
{
var m = new M();
m.X += eventHandler;
m.OnFoo();
m.X -= eventHandler;
m.OnFoo();
}

public async Task FooAsync(object sender, EventArgs e)
{
await Task.Delay(1000);
Debug.WriteLine("Yay event handler");
}

public class M
{
public event EventHandler X;
public void OnX()
{
// If you're using C#-6, then X?.Invoke(null, EventArgs.Empty);

var localX = X;
if (localX != null)
localX(null, EventArgs.Empty);
}
}

Edit:

@svick suggests perhaps another solution, to simply make the method async void and register it directly, which is definitely shorter:

public async void SomeOtherEventHandler()
{
var m = new M();
m.X += FooAsync;
m.OnFoo();
m.X -= FooAsync;
m.OnFoo();
}

public async void FooAsync(object sender, EventArgs e)
{
await Task.Delay(1000);
Debug.WriteLine("Yay event handler");
}

How to remove all event handlers from an event

I found a solution on the MSDN forums. The sample code below will remove all Click events from button1.

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();

button1.Click += button1_Click;
button1.Click += button1_Click2;
button2.Click += button2_Click;
}

private void button1_Click(object sender, EventArgs e) => MessageBox.Show("Hello");
private void button1_Click2(object sender, EventArgs e) => MessageBox.Show("World");
private void button2_Click(object sender, EventArgs e) => RemoveClickEvent(button1);

private void RemoveClickEvent(Button b)
{
FieldInfo f1 = typeof(Control).GetField("EventClick",
BindingFlags.Static | BindingFlags.NonPublic);

object obj = f1.GetValue(b);
PropertyInfo pi = b.GetType().GetProperty("Events",
BindingFlags.NonPublic | BindingFlags.Instance);

EventHandlerList list = (EventHandlerList)pi.GetValue(b, null);
list.RemoveHandler(obj, list[obj]);
}
}

Unregistering Event with -=-Operator

If you realy want this behaviour ( I don't think, this is good pattern, bud its not matter ), you can derive from EventArgs class and add property for author of deletion.
Then you can do:

c.Delete( this ); //this = window
// ...
void business_Deleted(object sender, EventArgs e) {
bool isDeletedFromMe = false;
if ( e is DeletedEventArgs ) { isDeletedFromMe = object.ReferenceEquals( this, e.Author ); }
if ( false == isDeletedFromMe ) {
MessageBox.Show("Item has been deleted in another editor window.",
"...", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Close();
}
}

or you can do it thiw way:

void business_Deleted(object sender, EventArgs e)
{
if ( false == object.ReferenceEquals( sender, this.currentlyDeletingBusiness ) ) {
MessageBox.Show("Item has been deleted in another editor window.",
"...", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
Close();
}

Business currentlyDeletingBusiness;
private void deleteButton_Activate(object sender, EventArgs e)
{
Business c = (Business)businessBindingSource.DataSource;
try {
this.currentlyDeletingBusiness = c;
c.Delete();
}
finally {
this.currentlyDeletingBusiness = null;
}
}

Unregister event handler in MonoBehaviour

Start() method will not get called unless the behavior is enabled. And it can only be enabled if the game object that it's on is activated. So, you can safely assume that if the behavior gets OnDestroy() called, it did get a call to Start() earlier.

The destruction of mono behaviour happens within the same frame (before rendering). However, if you are not just asking this because of precaution for memory leaks, but your logic depends on that, something is definitely wrong with it.

As @Programmer correctly noted, your code, most likely, should subscribe and unsubscribe to events in OnEnabled and OnDisabled instead. This follows the concept of component design pattern much better: after all, this pattern assumes (and quite logically so) that components only do their work when deliberately activated by outside systems, and go back to sleep when deactivated. Now, Unity developers have to bend these guidelines a little in a lot of places, because MonoBehaviours are almost the only point of entry they have in terms of C# code, but still, it's better to follow the implicit rules of the system.

And finally, I honestly never saw considerable performance problems in a Unity project because of memory leaks. It is a good that you thinking about it, but your time would be considerably better spent if you eliminated instantiation and destruction of the objects altogether, by moving them to use an object pool, as they are much more often tend to be a source of problems.

C# How to properly remove event handler code?

thats because in C#, the functions are added to the events programatically by the designer, when you add it in the designer.

In the solution explorer window, Expand Form1.cs and open the Form1.Designer.cs file there:

Sample Image

then locate the function :

private void InitializeComponent()

and delete the line that registers your event handler to the event. it will have a += operator in the middle like this:

Sample Image

your event will be located at line 66 and will look like this:

this.txtInputBox.KeyDown += new System.EventHandler(this.txtInputBox_KeyDown);


Related Topics



Leave a reply



Submit