Unsubscribing from the event in delegate vs function [duplicate] - c#

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();
}

Related

Use "this" referring anonymous delegate

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;
}
};

C# - Use a delegate to run a method at the same time every hour [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Run a code in given time interval
I have a method that runs on the hour. I am currently doing this like so:
while (true)
{
string rightNow = DateTime.Now.ToString("mm");
if (rightNow == "00")
{
RunMyMethod();
}
Thread.Sleep(10000);
}
I have been reliably informed by a friend that I'm an idiot (and generally a bad person), and that I should be using events and delegates to do this instead. I get that, outside the scope of any methods I need declare a delegate with the same return type as my method, and with the same input parameters like so:
public delegate void MyMethodsDelegate();
Then, when I want to use my delegate I instantiate it in the same way I instanciate a class like so:
MyMethodsDelegate foobar = new MyMethodsDelegate(RunMyMethod);
Then I can invoke my delegate like so:
foobar.Invoke();
Ok so I have my delegate setup, how about the event? The event I want to create is 'It's x o'clock', then every time it's x o'clock this kicks off my delegate
Also, am I setting up my delegate correctly?
Thanks
You can use System.Timers.Timer class which executes a delegate asynchronously and raises the Elapsed event when given time interval is elapsed. I would strongly recommend you reading MSDN remarks part for the Timer class, see here.
System.Timers.Timer timer = new Timer(60 * 60 * 100);
Case 1: Subscribe to anonymous method
timer.Elapsed += (s, e) =>
{
// callback code here
};
Case 2: Subscribe to explicitly defined delegate
ElapsedEventHandler handler = (s, e) =>
{
// callback code here
};
timer.Elapsed += handler;
Case 3: Subscribe to the existing method
timer.Elapsed += this.OnHourElapsed;
private void OnHourElapsed(object sender, EventArgs args)
{
// callback code here
}
Then finally just start a timer
timer.Start();

What happens if I call a event handler declared inside a function that is not being executed?

Something like this:
void SomeFunc()
{
int InsideVar = 1;
EventHandler handler = (s, e) => { MessageBox.Show(InsideVar.ToString()); };
SomeEvent += handler;
}
And then SomeEvent is invoked after SomeFunc is executed. I actually tested it and it worked but cannot understed why. I thought InsideVar would be on the stack and would stop existing after the function is executed. I was expecting an exception. Can someone clarify this please?
You've just discovered closures.

C# - anonymous functions and event handlers

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").

How do I deregister an anonymous handler?

C# 2.0 has a neat feature called anonymous functions. This is intended to be used mostly with events:
Button.Click += delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };
Now, suppose that Button is a static member, then adding delegates to it would count as unmanaged resources. Normally, I would have to deregister the handler before regestring it again. This is a pretty common use case for GUI programming.
What are the guidelines with anonymous functions? Does the framework deregrister it automatically? If so, when?
No, anonymous functions will not get deregistered automatically. You should make sure to do it yourself, if the event should not be hooked up for the whole lifetime of your application.
To do this, of course, you would have to store the delegate reference, to be able to de-register it.
Something like:
EventHandler handler = delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };
Button.Click += handler;
// ... program code
Button.Click -= handler;
Also, see this question.
If I recall correctly (and I can recall where I read this) inline anonymous delegates cannot be removed.
You would need to assign to a (static) delegate field.
private static EventHandler<EventArgs> myHandler = (a,b) => { ... }
myButton.Click += myhandler;
...
myButton.Click -= myHandler;

Categories

Resources