Removing anonymous event handler - c#

I have the following code where SprintServiceClient is a reference to a WCF Service-
public class OnlineService
{
private SprintServiceClient _client;
public OnlineService()
{
_client = new SprintServiceClient();
}
public void AddMemberToTeam(MemberModel user, int projectId, Action<int> callback)
{
_client.AddMemberToTeamCompleted += (s, e) => callback(e.Result);
_client.AddMemberToTeamAsync(user.ToUser(), projectId);
}
}
the problem is that every time AddMemberToTeam is called it adds another callback to client.AddMemberToTeamCompleted
i.e the first time AddMemberToTeam is called the callback is called once, the second time AddMemberToTeam is called the callback is called twice ect.
Is there any way to remove the eventhandler from AddMemberToTeamCompleted once the eventhandler has been called or use another method which takes in the callback?

You can refer to your anonymous method from inside itself as long as you assign a delegate to a variable first:
EventHandler<SomeEventArgs> handler = null;
handler = (s, e) =>
{
_client.AddMemberToTeamCompleted -= handler;
callback(e.Result);
};
_client.AddMemberToTeamCompleted += handler;
Note that you need to declare the variable and assign it separately or the compiler will deem it uninitialized when you come to use it inside the method body.

The trick to making a self-unsubscribing event-handler is to capture the handler itself so you can use it in a -=. There is a problem of declaration and definite assignment, though; so we can't do something like:
EventHandler handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler; // <===== not yet defined
};
So instead we initialize to null first, so the declaration is before the usage, and it has a known value (null) before first used:
EventHandler handler = null;
handler = (s, e) => {
callback(e.Result);
_client.AddMemberToTeamCompleted -= handler;
};
_client.AddMemberToTeamCompleted += handler;

No there is no way,
Apparantly Tim and Marc have another nice solution
But you can always just name them, and do the -= on the named eventhandler on this method ;)
Guessing your event:
_client.AddMemberToTeamCompleted += OnAddMemberToTeamCompleted;
and
public void OnAddMemberToTeamCompleted(object sender, EventArgs args)
{
_client.AddMemberToTeamCompleted -= OnAddMemberToTeamCompleted;
callback(e.Result)
}
Next problem is getting this callback in your listener. Perhaps putting it on a Property in the EventArgs (but that feels kinda dirty, I agree)

Related

C# add extra parameter in the handler of an event

In C#/Unity, is there a way to pass more arguments to an event handler that were not specified in the delegate?
Assume that I cannot modify the delegate. (It comes from an external class.)
I need to be able to do this so I can unsubscribe from my handler later (onClick -= HandleClick somewhere else in the code).
Example:
delegate void A(str s);
event A onClick;
Awake() {
onClick += HandleClick;
}
void HandleOnClick(str s, int a) {
// How to get access to this second argument a here?
}
I know I can do the following:
int a = 10;
onClick += (s) => HandleOnClick(s, 10);
But this won't let me unregister the lambda (since it's an anonymous delegate) if I wire it up this way.
One way you can try to use a variable to hold the delegate reference which is able to subscribe or unsubscribe from the event.
Awake() {
int a = 10;
var e1 = (s) => HandleOnClick(s, a);
onClick += e1;
onClick -= e1;
}

Handling events with C# Extension Methods

I recently learned about using C# extension methods to make calling events easier and I've been using them more and more. I recently hit a strange issue that I don't understand though, and I was wondering if someone could explain it.
The issue occurs when trying to set an eventhandler extension method as an event handler of another event. Here is an example of what I'm doing:
public static class EventHandlerExtensions
{
public static void Raise<TEventArgs>(
this EventHandler<TEventArgs> eventHandler,
object sender, TEventArgs args) where TEventArgs:EventArgs
{
if (eventHandler != null)
{
eventHandler(sender, args);
}
}
}
public class Test
{
private event EventHandler<EventArgs> EventA;
private event EventHandler<EventArgs> EventB;
public Test()
{
Console.WriteLine("::Start");
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
}
}
In this example, EventA should be triggered as a result of EventB being triggered. However, when I run this code, EventB fires, but the extension method on A doesn't find any listeners for it.
If I change the order around, everything works fine:
Console.WriteLine("::Start");
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB += EventA.Raise;
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
Also, calling EventA.Raise from a lambda works fine:
Console.WriteLine("::Start");
EventB += (s, a) => EventA.Raise(s, a);
EventA += (s, a) => Console.WriteLine("Event A raised");
EventB.Raise(this, EventArgs.Empty);
Console.WriteLine("::End");
This is just a simple example, but I'm trying to create a class which can re-dispatch events of event sources added to it in the cleanest way possible. I don't want to create named methods just for redispatching the same events, and I'd rather not store lists of lambda functions that I can unhook from the event handlers later. Mostly, I'm just curious as to why this is happening?
Any ideas?
You capture old value of EventA into the closure by your Raise function. Since later you use += it changes value of EventA, but your closure still have an old value.
You code:
EventB += EventA.Raise;
EventA += (s, a) => Console.WriteLine("Event A raised");
Can be expanded into equivalent code which makes it clear why you get old delegate:
var oldEventA = EventA;
EventB += oldEventA.Raise; // captures old value here
// now EventA changed to new value
EventA = oldEventA + ((s, a) => Console.WriteLine("Event A raised");)
You can add following to before EventB += EventA.Raise to verify that code actually raises old event for A:
EventA += (s, a) => Console.WriteLine("Old Event A raised");
Delegate objects are immutable. Much like strings. So when you assign EventA, you create a new object. EventB is still targeting the old one, the one that didn't have any event handler assigned yet. You have to swap the two statements to fix the problem.

Detaching an handler the first time you call it

I would like to call the event handler just one time, and then detach it. I tried to write:
EventHandler handler = (s, e) =>
{
// Do something
//
// blabla
// Detach the handler
SizeChanged -= handler;
};
SizeChanged += handler;
However on the line SizeChanged -= handler I get this error
Use of unassigned local variable 'handler'
Do you have idead on how I should proceed ? I thought about using a boolean flag, but I will do that only if I can't find a way to detach the handler.
The C# compiler will first create the lambda expression you wrote before assigning the result to the variable. So when the lambda is defined, handler doesn't have a value.
It works though if you assign a value of null to EventHandler before.
Since it's a closure and local variables are captured in the closure, at the time of the call handler will have the correct value and it will work:
EventHandler handler=null;
handler = (s, e) =>
{
// Do something
SizeChanged -= handler;
};
SizeChanged += handler;
To all people downvoting: It won't cause a NullReferenceException. handler is a local variable which is captured in the closure, so the value of handler inside the lambda will change, when it changes in the method that contains the closure. I tested it actually on my PC and it works perfectly.
This is because it really is unassigned yet. Try making a named method out of it, so the symbol is known prehand.
private void OnEvent(object sender, EventArgs e)
{
// Do something
AnEvent -= OnEvent;
}
private void RegisterOnce()
{
AnEvent += OnEvent;
}
I would also recommend to run the DoSmething code only after detatch and implement some locking mechanism, in case you have multithrading, to prevent from multiple threads call the event at the exact same time, not having time to detatch and therefore, all run.

How to remove the event?

For Sample ....
SampleClass :
public class SampleClass
{
public delegate void BeforeEditorHandle();
public event BeforeEditorHandle OnBeforeEditor;
}
MainMethod
static void Main(string[] args)
{
SampleClass sc = new SampleClass();
// Add Event
sc.OnBeforeEditor +=new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
// Remove Event
sc.OnBeforeEditor -= new SampleClass.BeforeEditorHandle(sc_OnBeforeEditor);
}
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
But I think this is very ugly when I have too much sources in the event....
Can anybody tell me the best way to remove the event please ?
You can assign the event handler/lambda to a variable which you can then subscribe and unsubscribe:
var myHandler = () => { };
sc.OnBeforeEditor += myHandler;
sc.OnBeforeEditor -= myHandler;
I'm pretty sure your code here won't work:
And , if I add the event by dynamic like this ...↓
sc.OnBeforeEditor += () => { };
Should I remove the event like ↓
sc.OnBeforeEditor -= () => { };
This is because restating the lambda creates a new different lambda.
You need to store the old reference and use it to unsubscribe:
BeforeEditorHandle myHandler=() => { }
sc.OnBeforeEditor += myHandler;
...
sc.OnBeforeEditor -= myHandler;
For easier unsubscribing you can collect your event handlers in a collection (For example List<BeforeEditorHandle>).
From MSDN:
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. For more information about
anonymous functions, see Anonymous
Functions (C# Programming Guide).

How do I Unregister 'anonymous' event handler [duplicate]

This question already has answers here:
Unsubscribe anonymous method in C#
(14 answers)
Closed 8 years ago.
Say if I listen for an event:
Subject.NewEvent += delegate(object sender, NewEventArgs e)
{
//some code
});
Now how do I un-register this event? Or just allow the memory to leak?
Give your instance of the anonymous delegate a name:
EventHandler<NewEventArg> handler = delegate(object sender, NewEventArgs e)
{
//some code
};
Subject.NewEvent += handler;
Subject.NewEvent -= 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.
To remove the handler on first invocation:
//SubjectType Subject = ..... already defined if using (2)
EventHandler handler = null;
handler = delegate(object sender, EventArgs e)
{
// (1)
(sender as SubjectType).NewEvent -= handler;
// or
// (2) Subject.NewEvent -= handler;
// do stuff here
};
Subject.NewEvent += handler;
You can create method for unregistering from all listeners of event. This not exactly what you whant, but sometimes it can be helpfull. For example (this really works =)) :
class Program {
static void Main(string[] args) {
A someClass = new A();
someClass.SomeEvent += delegate(object sender, EventArgs e) {
throw new NotImplementedException();
};
someClass.ClearEventHandlers();
someClass.FireEvent();
Console.WriteLine("No error.");
}
public class A {
public event EventHandler SomeEvent;
public void ClearEventHandlers() {
Delegate[] delegates = SomeEvent.GetInvocationList();
foreach (Delegate delegate in delegates) {
SomeEvent -= (EventHandler) delegate;
}
}
public void FireEvent() {
if (SomeEvent != null) {
SomeEvent(null, null);
}
}
}
}
You need a name for your anonymous function, and then, you can only do it as long as the name is in scope:
var handler = new EventHandler(delegate(object o, EventArgs e)
{
//do something...
};
Subject.NewEvent += handler;
// later on while handler is still in scope...
Subject.NewEvent -= handler;
Do you need to un-register it for a reason other than leakage?
Regarding the "Or just allow the memory to leak" bit, when Subject is cleaned up by the Garbage Collector, your anonymous delegate should be cleaned up as well, so there shouldn't be a leak.
There is another question (of mine) which goes into this in some (too much) detail: Weak event handler model for use with lambdas.
However, now that the Reactive Framework has come out, I'd seriously consider looking into that in this kind of situation.

Categories