How to use the this (or something of that kind) referring to the delegate instance instead of the class instance ?
instance.OnEventFoo += delegate()
{
if (condition)
{
instance.OnEventBar += this;
}
};
Since you can't refer to a variable before it is declared, you have to:
first declare the variable,
then assign a delegate,
then register the handler with the event.
// Add an anonymous delegate to the events list and auto-removes automatically if item disposed
DataRowChangeEventHandler handler = null;
handler = (sender, args) =>
{
if (condition)
{
// need to remove this delegate instance of the events list
RowChanged -= handler;
}
};
something.RowChanged += handler;
You need to store it in a variable somewhere. For example:
EventHandler rowChanged = null; // to avoid "uninitialized variable" error
rowChanged = (s, e) =>
{
if (condition)
{
// this will unsubscribe from the event as expected
RowChanged -= rowChanged;
}
};
Related
This question already has an answer here:
How to remove a lambda event handler [duplicate]
(1 answer)
Closed 1 year ago.
Why C# allows to unsubscribe from the event when the event handler is defined as a function, but not when the event handler is defined as a delegate?
Consider the following code that works:
void SomeFunction()
{
var eventRaiser = ClassRaisingEvent.GetEventRaiser();
void handler(object sender, EventArgs ev)
{
ProcessData(ev);
eventRaiser.OnEvent -= handler;
}
eventRaiser.OnEvent += handler
eventRaiser.Process();
}
But this fails to compile at the indicated spot:
void SomeFunction()
{
var eventRaiser = ClassRaisingEvent.GetEventRaiser();
DelegateType handler = (object sender, EventArgs ev) =>
{
ProcessData(ev);
eventRaiser.OnEvent -= handler; // FAILS here with "Use of unassigned local variable 'handler '"
}
eventRaiser.OnEvent += handler
eventRaiser.Process();
}
EDIT: This question is not how to unsubscribe. This is why (in technical sense) function name is captured in the scope of the function, but the delegate's isn't.
EDIT2: Answer here (and esp. pt. 2 in the answer) explains the behavior I'm seeing.
The local function is defined at compile time, just like other functions. The handler variable, however, is only known at runtime. So, as the compiler tells you, you cannot use the handler variable before it's been assigned a value, which means after the closing } of your delegate.
What you could do to get your second snippet working, is to initialize the variable first:
void SomeFunction()
{
var eventRaiser = ClassRaisingEvent.GetEventRaiser();
DelegateType handler = null;
handler = (object sender, EventArgs ev) =>
{
ProcessData(ev);
eventRaiser.OnEvent -= handler;
};
eventRaiser.OnEvent += handler;
eventRaiser.Process();
}
In C# 5, what is the behavior of the -= operator when unsubscribing from events.
Assume subscribing to the same event multiple times is valid for this application logic, such as follows:
Property_Saved += Property_Saved_Handler;
Property_Saved += Property_Saved_Handler;
Property_Saved += Property_Saved_Handler;
Now we are subscribed three times.
After unsubscribing with the following one line of code:
Property_Saved -= Property_Saved_Handler;
How many subscriptions are left? 2? none? ...?
Two are left after that. Each -= only removes one subscription. At least, that's the case if it's using just a regular delegate to back the event.
You can see this easily without really involving events:
using System;
public class Program
{
public static void Main(string[] args)
{
Action action = () => Console.WriteLine("Foo");
// This is a stand-in for the event.
Action x = null;
x += action;
x += action;
x += action;
x -= action;
x(); // Prints Foo twice
}
}
Strictly speaking, an event subscription could do anything. You could implement an event like this:
private EventHandler weirdEvent;
public event EventHandler WeirdEvent
{
add { weirdEvent += value; } // Subscribe as normal
remove { weirdEvent = null; } // I'm bored with *all* the handlers
}
But normally events just delegate to Delegate.Combine and Delegate.Remove, which are the methods that += and -= are syntactic sugar for in C#.
My article on events and delegates contains more details about exactly what happens with combination and removal.
private void button1_Click(object sender, EventArgs e)
{
// set breakpoint
}
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button1.Click -= new System.EventHandler(this.button1_Click);
Invoking the click event will show the breakpoint hit twice.
This should be also safe.
Property_Saved += Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Property_Saved -= Property_Saved_Handler;
Just make your own test using GetInvocationList
public delegate void MyEventHandler(string s);
public event MyEventHandler MyEvent;
MyEventHandler #event = s => { };
MyEvent += #event;
Console.WriteLine(MyEvent.GetInvocationList().Length);
MyEvent += #event;
Console.WriteLine(MyEvent.GetInvocationList().Length);
MyEvent -= #event;
Console.WriteLine(MyEvent.GetInvocationList().Length);
This will print
1
2
1
I have a "MyForm" that derives from System.Windows.Form.Form. MyForm handles the FormClosed event whose handler I set up in the constructor. Is it possible for an instantiated MyForm object to successfully remove my MyClass#FormClosed handler using -=? I am using an anonymous method to wire my FormClosed event if that matters.
// In MyForm Constructor, this ALWAYS gets fired...
FormClosed += (_sender, _args) => { m_mutex.Dispose(); m_mutex = null; };
// In Instantiating Class
MyForm form = new MyForm();
form.FormClosed += (_sender, _args) => { };
// I have also tried using "delegate(object sender, EventArgs e)" and neither worked.
Yes, you can remove it - but if you've used an anonymous function, it's slightly trickier.
Basically you'll either need to stop using an anonymous function (i.e. put the behaviour into a method) or you'll need to have a field to store the value of the delegate:
private EventHandler formClosedHandler;
...
// In constructor
formClosedHandler = (sender, args) => { ... };
FormClosed += formClosedHandler;
// Later...
FormClosed -= formClosedHandler;
formClosedHandler = null;
Personally I'd favour moving the logic into a method unless it's really tricky for some reason.
Yes, you can unsubscribe from any of the events, even in the Disposed() method.
I have the following code:
public List<IWFResourceInstance> FindStepsByType(IWFResource res)
{
List<IWFResourceInstance> retval = new List<IWFResourceInstance>();
this.FoundStep += delegate(object sender, WalkerStepEventArgs e)
{
if (e.Step.ResourceType == res) retval.Add(e.Step);
};
this.Start();
return retval;
}
Notice how I register my event member (FoundStep) to local in-place anonymous function.
My question is: when the function 'FindStepByType' will end - will the anonymous function be removed automatically from the delegate list of the event or I have to manually remove it before steping out the function? (and how do I do that?)
I hope my question was clear.
Your code has a few problems (some you and others have identified):
The anonymous delegate cannot be removed from the event as coded.
The anonymous delegate will live longer than the life of the method calling it because you've added it to FoundStep which is a member of this.
Every entry into FindStepsByType adds another anonymous delegate to FoundStep.
The anonymous delegate is a closure and effectively extends the lifetime of retval, so even if you stop referencing retval elsewhere in your code, it's still held by the anonymous delegate.
To fix this, and still use an anonymous delegate, assign it to a local variable, and then remove the handler inside a finally block (necessary in case the handler throws an exception):
public List<IWFResourceInstance> FindStepsByType(IWFResource res)
{
List<IWFResourceInstance> retval = new List<IWFResourceInstance>();
EventHandler<WalkerStepEventArgs> handler = (sender, e) =>
{
if (e.Step.ResourceType == res) retval.Add(e.Step);
};
this.FoundStep += handler;
try
{
this.Start();
}
finally
{
this.FoundStep -= handler;
}
return retval;
}
With C# 7.0+ you can replace the anonymous delegate with a local function, achieving the same effect:
public List<IWFResourceInstance> FindStepsByType(IWFResource res)
{
var retval = new List<IWFResourceInstance>();
void Handler(object sender, WalkerStepEventArgs e)
{
if (e.Step.ResourceType == res) retval.Add(e.Step);
}
FoundStep += Handler;
try
{
this.Start();
}
finally
{
FoundStep -= Handler;
}
return retval;
}
Below is approach about how unsubscribe event in anonymous method:
DispatcherTimer _timer = new DispatcherTimer();
_timer.Interval = TimeSpan.FromMilliseconds(1000);
EventHandler handler = null;
int i = 0;
_timer.Tick += handler = new EventHandler(delegate(object s, EventArgs ev)
{
i++;
if(i==10)
_timer.Tick -= handler;
});
_timer.Start();
No, it will not be removed automatically. In this sense, there's not a difference between an anonymous method and a "normal" method. If you want, you should manually unsubscribe from the event.
Actually, it'll capture other variables (e.g. res in your example) and keep them alive (prevents garbage collector from collecting them) too.
When using an anonymous delegate (or a lambda expression) to subscribe to an event does not allow you to easily unsubscribe from that event later. An event handler is never automatically unsubscribed.
If you look at your code, even though you declare and subscribe to the event in a function, the event you are subscribing to is on the class, so once subscribed it will always be subscribed even after the function exits. The other important thing to realize is that each time this function is called, it will subscribe to the event again. This is perfectly legal since events are essentially multicast delegates and allow multiple subscribers. (This may or may not be what you intend.)
In order to unsubscribe from the delegate before you exit the function, you would need to store the anonymous delegate in a delegate variable and add the delegate to the event. You should then be able to remove the delegate from the event before the function exits.
For these reasons, if you will have to unsubscribe from the event at some later point it is not recommended to use anonymous delegates. See How to: Subscribe to and Unsubscribe from Events (C# Programming Guide) (specifically the section titled "To subscribe to events by using an anonymous method").
I was wiring up an event to use a lambda which needed to remove itself after triggering. I couldn't do it by inlining the lambda to the += event (no accessable variable to use to remove the event) so i set up an Action<object, EventArgs> variable and moved the lambda there. The main error was that it could not convert an Action<object, EventArgs> to an EventHandler. I thought lambda expressions were implicitly convertable to event handlers, why doesn't this work?
Action<Object, EventArgs> a = (o, ea) => { };
EventHandler e = a.Invoke;
Lambdas are implicitly convertible to delegate types with the right shape, but two same-shaped delegate types are not implicitly convertible to one another. Just make the local variable have type EventHandler instead.
EventHandler h = (o, ea) => { ... };
e += h;
...
e -= h;
(in case it helps:
Action<object, EventArgs> a = (o, ea) => { };
EventHandler e = a; // not allowed
EventHandler e2 = (o,ea) => a(o,ea); // ok
)
Declare your event as
public event Action<object, EventArgs> e;
Then you can directly add your action:
Action<object, EventArgs> a = something;
e += a;
In general, delegates can't be cast because they have no inheritance tree defining which casts are valid. To that end, you have two choices:
Use a variable of type EventHandler instead of the Action<T1, T2>
Use an inline declaration.
// option 1: local variable
EventHandler eh = (o, ea) => { /* [snip] */ };
obj.event += eh;
obj.event -= eh;
// option 2: inline declaration
obj.event += (o, ea) => { /* [snip] */ };
You can use an anonymous method instead:
Event += (sender, e) =>
{
// Multiple lines
// of code here
};