Adding and Removing Anonymous Event Handler

C#EventsAnonymous Methods

C# Problem Overview


I was wondering if this actually worked ?

private void RegisterKeyChanged(T item) 
{
    item.OnKeyChanged += (o, k) => ChangeItemKey((T)o, k);
}

private void UnRegisterKeyChanged(T item) 
{
    item.OnKeyChanged -= (o, k) => ChangeItemKey((T)o, k);
}

How does the compiler know that the event handlers are the same ? Is this even recommended?

C# Solutions


Solution 1 - C#

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.

Solution 2 - C#

For anyone interested, you can add and remove an anonymous event handler like this

public class Musician
{
	public void TuneGuitar()
	{
		Metronome metronome = new Metronome();

		EventHandler<EventArgs> handler = null;
		handler = (sender, args) =>
		{
			// Tune guitar
			// ...
			
			// Unsubscribe from tick event when guitar sound is perfect
			metronome.Tick -= handler;
		};
		
		// Attach event handler
		metronome.Tick += handler;
	}
}

public class Metronome
{
	event EventHandler<EventArgs> Tick;
}

UPDATE: In C# 7.0 we have supports for local functions so the TuneGuitar method can now be written as:

public void TuneGuitar()
{
	Metronome metronome = new Metronome();

	void handler(object sender, EventArgs args)
	{
		// Tune guitar
		// ...
		
		// Unsubscribe from tick event when guitar sound is perfect
		metronome.Tick -= handler;
	};
	
	// Attach event handler
	metronome.Tick += handler;
}

Solution 3 - C#

If you need to unsubscribe an event handler, you'll need to have a definite reference to a concrete delegate. Looking at Delegate.Equality you will find that delegates aren't just compared using reference equality, however this doesn't matter for anonymous delegates.

For an anonymous delegate, the compiler (basically) just creates a new "non-anonymous" delegate for each anonymous delegate, even if the delegate bodies are the same. Because of this, the framework will not find the delegate to unsubscribe when you use the code example you gave.

Solution 4 - C#

That won't work I'm afraid, since the two lambda expressions (and delegates) that you declared are actually different objects, and return different references. Hence, the removal of the handler (-=) will always fail.

The common solution to this problem (where you need to remove the handler) is simply to refactor the lamba expression into a proper method. An alternative is to maintain a class variable for the event handler delegate, and add and remove this, though I am personally not a fan of it. (It's more hassle than just creating a normal method, if anything.)

Solution 5 - C#

I don't believe this will work. If you really need to unregister from an event you must specify an explicit event handler which you can later unregister from instead of an anonymous delegate.

Solution 6 - C#

If you check with the document for Delegate.Equality, you would find out they are not compared by reference.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHaxElitView Question on Stackoverflow
Solution 1 - C#Ryan LundyView Answer on Stackoverflow
Solution 2 - C#sb.olofssonView Answer on Stackoverflow
Solution 3 - C#Dan HerbertView Answer on Stackoverflow
Solution 4 - C#NoldorinView Answer on Stackoverflow
Solution 5 - C#Dan AuclairView Answer on Stackoverflow
Solution 6 - C#CodismView Answer on Stackoverflow