Anonymous method as event handler - c#

Edit: this question is not about how to unsubscribe from events because I know how to do that if I want to. The question is about if there is a conflict with garbage collection in my specific scenario.
In some code I wrote a while ago I registered an event (CheckExecution, owned by isoDataTemp, see code below) by means of an anonymous method. But now it has come to my mind that assigning an anonymous method to an event is pretty evil because there is no way to unregister the method from the event, right? I once had a problem with an object that could not be garbage collected because an event was still pointing to it.
So I wonder if I can get into trouble with it here. My spontaneous reaction is "no, because the anonymous method belongs to the MainForm and this has longer lifetime than its isoDataTemp member". But I am not sure. What about closures? Does the anonymous method belong to MainForm at all. I am confused...
public partial class MainForm : Form
{
// ...
void BackgroundWorkerISOAnalysisDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
int prog = 0;
isoDataTemp.CheckExecution += () =>
{
prog += 12;
if (prog > 100) prog = 100;
worker.ReportProgress(prog);
return !worker.CancellationPending;
};
isoDataTemp.Analyze();
if (worker.CancellationPending) e.Cancel = true;
}
}

To unregister all event handlers for an event that you own, simply use this code:
isoDataTemp.CheckExecution = null;
Note that this works for anonymous and non-anonymous event handlers.
From your example, you can add this code to your OnFormClosing event, to ensure that events assign to your isoDataTemp object are properly and completely removed.
Note that if the isoDataTemp object is a child object of the form object, then it will be removed when your Form object is destroyed and will be garbage-collected along with the destroyed Form object instance, as expected.

Related

Difference between registering delegate and registering named method for event

I would like to confirm something - when I register a method as a subscriber to an event the long way, like so:
_serviceContext.ReadingEntity += new EventHandler<ReadingWritingEntityEventArgs>(_serviceContext_ReadingEntity);
I need to un-register that method from the event's subscription if I don't want it to continue to be called when the event is fired, like this:
_serviceContext.ReadingEntity -= new EventHandler<ReadingWritingEntityEventArgs>(_serviceContext_ReadingEntity);
When I register a delegate as a subscriber to an event, like so:
public guy ThisMethod()
{
_serviceContext.ReadingEntity += delegate(object sender, ReadingWritingEntityEventArgs e)
{
};
}
There is no way to un-register that delegate from the subscriber list from that method. So I am assuming that the scope of this registration is limited to the method in which it's registered - i.e. if the _serviceContext.ReadingEntity event was fired in a method called by ThisMethod, this registration would be already expired and the code inside the delegate would not run. Is this correct?
Thanks!
p.s. I realize that the first "long" way of registering an event handler also has scope limitations, but I'm a bit hazy on that. My main question, however, is whether or not the delegate registration will live on outside of the method above.
Once you subscribed a delegate this way (you can unsubscribe using -= operator if you cache the delegate variable somewhere) you can't remove it from subscribers list and it will be invoked every time event rises until publisher is alive. Moreover this subscription will prevent any subscriber class (class which contains the method you subscribed to event) from garbage collection until publisher is alive (unless you use a static method).
To give some clarity, there is no "anonymous" methods in IL code. All your delegates and lamdbas are translated into static/instance methods and closure classes (depends on whether they use instance members/function parameters).
You can unsubscribe a delegate if you keep a reference to it.
EventHandler<ReadingWritingEntityEventArgs> aDelegate = delegate(object sender, ReadingWritingEntityEventArgs e)
{
};
_serviceContext.ReadingEntity += aDelegate;
_serviceContext.ReadingEntity -= aDelegate;
If you do not do it this way, there is no way to unsubcribe. The scope is not limited to the method in which it was registered. It will be registered for the lifetime of the event.

Events and Delegates Order when event is triggered

I recently attended a interview in C# where i was asked a question about Events and delegates in C#
The person asked me when a event say button gets clicked, which gets called first event or delegate?
Does the delegate calls the event or the event calls delegate?
Can we have a event without a delegate in c#?
The person asked me when a event say button gets clicked, which gets called first: the event or the delegate?
When you open a door, which gets opened first: the door or the doorknob?
Huh?
That question doesn't make any sense. You open a door with a doorknob, but you don't open a doorknob.
What does it mean to "call" an event? Events aren't things that you call. Events are things that you raise. Raising an event is identical with calling the delegate.
Does the delegate calls the event or the event calls delegate?
Does the door open the doorknob, or does the doorknob open the door?
Again, the question doesn't make sense. A doorknob is not something that can be "opened", and the doorknob does not open the door -- you open the door, by holding the doorknob.
Does the delegate call the event? No; events are not things that can be "called". Does the event call the delegate? No, the code that is raising the event calls the delegate.
Can we have a event without a delegate in c#?
Yes, in the sense that the delegate reference associated with an event can be a null reference. But every event is associated with a delegate type, and somehow has associated with it a reference to a delegate.
The whole set of questions indicates to me that the questioner does not have a very good understanding of the relationship between events and delegates. A good way to think about it is that an event is just a property that contains a reference to a multicast delegate. Whereas a property has special methods that get and set the value of the property, an event has special methods that add and remove delegates to the multicast delegate.
Events are a concept which utilize delegates as a means to call methods that have subscribed to them.
Events themselves are not called. When a piece of code raises an event, it will call each of the subscribed methods by invoking the delegates.
Events are raised, delegates are called. So when the button is clicked, a buttonClick event is raised, meaning that each delegate subscribed to the event will be called, according to the subscription order.
An event is simply a code construct implemented in .NET as a multi-cast delegate.
When an event is "raised" (which can only be done by code in the same class as the event; event raising must happen within "protected" scope), the delegates are invoked one at a time, in a synchronous fashion but not necessarily in any deterministic order. The event IS the delegate, so when the event is raised for a button being clicked, the delegates are invoked by the runtime, which has received the Windows message that the user clicked on the GUI area for the button.
The statements "the event is raised" and "the delegates are invoked" are equivalent statements; it's like asking "which comes first, the chicken or the gallus domesticus?".
Now, events often cascade, especially when we're talking about UI. There is a MouseUp event, invoked when the mouse button is released, which can fire one or more other events such as MouseClick, MouseDoubleClick, DragDrop, etc. You may not attach a handler to the MouseUp event, but there is built-in logic behind the scenes of MouseUp to raise the MouseClick event which you DO handle. So, in this sense, you could say that the MouseUp event "comes first" and calls the MouseClick handler delegate.
This page bubbled up on top of Google results, so below is something you might find useful if you also land here. Multi-cast delegates ARE called (see MSDN) in deterministic order on one thread, in the order of assignment. This assignment will involve an array of some sort, and it would be illogical for the indices to fill up out of order.
public partial class Form1 : Form
{
ob<Control>ob1;
ob<Control>ob2;
ob<Control>ob3;
public Form1()
{
InitializeComponent();
ob<Control>.setup(button1);
ob1 = new ob<Control>(1, true);
ob2 = new ob<Control>(2, false);
ob3 = new ob<Control>(3, false);
}
public class ob<T> where T : Control
{
int ndx;
Boolean isSentinel;
static Boolean dontdostuff;
static T c;
public static void setup(T c) {ob<T>.c = c;}//an argument less from constructor, useful for many objects (more likely to be menuitems)
public ob(int ndx, Boolean isSentinel)
{
this.ndx = ndx;
this.isSentinel = isSentinel;
c.Click += new EventHandler(click);
}
void click(object sender, EventArgs e)
{
if( isSentinel)
{
if (MessageBox.Show("ob" + ndx + " asks: short circuit subsequent delegate calls?", "", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
{
dontdostuff = true;
return;
}
else
{
dontdostuff = false;
}
}
else
{
if( dontdostuff) return;
}
MessageBox.Show("ob" + ndx + " doing stuff in order of handler addition", "", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}

In what cases are detaching from events necessary?

I'm not sure if I'm entirely clear on the implications of attaching to events in objects.
This is my current understanding, correct or elaborate:
1. Attaching to local class events do not need to be detached
Examples:
this.Closing += new System.ComponentModel.CancelEventHandler(MainWindow_Closing);
public event EventHandler OnMyCustomEvent = delegate { };
I'm assuming that when your object is disposed or garbage collected, the functions are deallocated and would automatically detach from the events.
2. Attaching to objects you no longer need (= null;) have to be detached from
Examples:
Attaching to a timer's Elapsed event, which you only respond to once. I would assume you need to store the Timer in a local variable so you can detached the Elapsed event after the event fires. Thus, declaring the timer in a local method scope like so would result in a leak:
System.Timers.Timer myDataTimer = new System.Timers.Timer(1000);
myDataTimer.Elapsed += new System.Timers.ElapsedEventHandler(myDataTimer_Elapsed);
3. Attaching to events in a local object to your class does not require disposing?
For example, if you had an ObservableCollection that your creates, monitors, and lets die. If you attached to the CollectionChanged event using a local, private function, wouldn't this function deallocate when your class is garbage collected, causing the ObservableCollection to also be freed?
I'm sure I have places where I've stopped using objects and have failed to detach from an event (for example, the timer example I made), so I'm looking for a clearer explanation on how this works.
I think you're making it more complicated than it needs to be. You just need to remember two things:
When you subscribe to an event, the event's "owner" (the publisher) generally keeps a reference to the delegate you subscribe with.
If you use an instance method as the action of a delegate, then the delegate has a reference to its "target" object.
This means that if you write:
publisher.SomeEvent += subscriber.SomeMethod;
Then subscriber won't be eligible for garbage collection before publisher is unless you unsubscribe later.
Note that in many cases, subscriber is just this:
publisher.SomeEvent += myDataTimer_Elapsed;
is equivalent to:
publisher.SomeEvent += this.myDataTimer_Elapsed;
assuming it's an instance method.
There is no reverse relationship just due to event subscription - in other words the subscriber doesn't keep the publisher alive.
See my article on events and delegates for more information, by the way.
The remaining references preventing garbage collection has one more effect that may be obvious but nontheless not yet stated in this thread; the attached event handler will be excuted as well.
I have experienced this a couple of times. One was when we had an application that gradually became slower and slower the longer it run. The application created the user interface in a dynamic fashion by loading user controls. The container made the user controls subscribe to certain events in the environment, and one of these were not unsubscribed from when the controls were "unloaded".
After a while this led to a large number of event listeners being executed each time that particular event was raised. This can of course lead to serious race conditions when a good number of "sleeping" instances suddenly wake up and try to act on the same input.
In short; if you write code to hook up an event listener; make sure that you release as soon as it's not needed any longer. I almost dare to promise it will save you from at least one headache at some point in the future.
The relevant case where you have to unsubscribe from an event is like this:
public class A
{
// ...
public event EventHandler SomethingHappened;
}
public class B
{
private void DoSomething() { /* ... */ } // instance method
private void Attach(A obj)
{
obj.SomethingHappened += DoSomething();
}
}
In this scenario, when you dispose of a B, there will still be a dangling reference to it from obj's event handler. If you want to reclaim the B's memory, then you need to detach B.DoSomething() from the relevant event handler first.
You could run into the same thing if the event subscription line looked like this, of course:
obj.SomethingHappened += someOtherObject.Whatever.DoSomething();
Now it's someOtherObject that's on the hook and can't be garbage collected.

EventHandlers and Anonymous Delegates / Lambda Expressions

I'm hoping to clear some things up with anonymous delegates and lambda expressions being used to create a method for event handlers in C#, for myself at least.
Suppose we have an event that adds either an anonymous delegate or a lambda expression (for you lucky crowds that can use newer versions of .NET).
SomeClass.SomeEvent += delegate(object o, EventArg e) { /* do something */ };
I have read that people in the past have forgotten about events that still have handlers which prevent the class from being garbage collected. How would one go about removing the added handler without just setting SomeEvent to null within the class. Wouldn't the following be an entirely new handler?
SomeClass.SomeEvent -= delegate(object o, EventArg e) { /* do something */ };
I could see storing the anonymous delegate or lambda expression in a variable. But that, to me at least, seems to defeat the entire purpose of being able to simply and succinctly add an event handler.
SomeEventDelegate handler = new SomeEventDelegate(delegate(object o, EventArg e) { /* do something */ });
SomeClass.SomeEvent += handler;
// ... stuff
SomeClass.SomeEvent -= handler;
Again, I understand that you could just do...
public override Dispose(bool disposing)
{
_someEvent = null;
this.Dispose();
}
But I'm more interesting with just removing the dynamically created method from the event. Hopefully someone can shed some light onto this for me. Thanks!
If object X has an event handler whose target is object Y, then object X being alive means that object Y can't be garbage collected. It doesn't stop object X from being garbage collected.
Normally when something is disposed, it will become garbage pretty soon anyway, which means you don't have a problem.
The problem with events and GC is if you forget to remove a subscribed handler from a different object - i.e. you have a listener which is disposed, but will never be garbage collected because there's still a reference to it from the event in a different object.
I think the problem is you seem to be proceeding from the assumption that having a delegate assigned to an object's event prevents it from being GCed.
This as a simple statement is not true.
With that said the perceived problem disappears.
Initially in garbage collection everything is garbage. The GC runs through every thing currently available globally and on each stack and from these those other objects that they are referencing and so on, marking each as not garbage.
How would such a graphing process manage to arrive at this object?
You can't.
Just like you can't create anonymous type outside of its scope (except for some compiler tricks).
That's why it's called anonymous.
You have to save a reference somewhere... or use reflection.

unsubscribing events from class?

I need bit of advise on best practice for this type of scenario. I searched but dont find any satisfying answer.
We use a 3rd party (.net) DLL in our winforms project. It raises some events and we subscribe to. Question is , do i need to explicitly unsubscribe these events in my class ?
BTW we both use .net framework 4. Thanks for the advise.
some sample code ...
public class MyClientCode: IDisposable
{
private readonly 3rdParty.TheirClass _theirClass;
public MyClientCode()
{
_theirClass = new _theirClass()
_theirClass.ReadData += ReadDataEvent;
}
public void SomeOtherMethod()
{
//some other code
}
public void ReadDataEvent()
{
//some code
}
public void Dispose()
{
_theirClass.ReadData -= ReadDataEvent;
}
}
and in the button click event, i do ...
MyClientCode code = new MyClientCode();
code.SomeOtherMethod();
If you don't unsubscribe, the object that subscribed to the event will not be garbage collected (it will be kept in memory). This can create memory leaks and lead to excessive memory usage.
However, if the event has a shorter or same lifetime as the class that contains it, in your case, the memory will be collected properly. If you have another object reference a non-private event, then you will run into issues.
See MSDN:
Until you unsubscribe from an event, the multicast delegate that underlies the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler. As long as the publishing object holds that reference, your subscriber object will not be garbage collected.
Note that you don't need to unsubscribe if you are exiting your application, only in cases where it shouldn't be held in memory. (For example, when you close a window in your application, you should unsubscribe from any events, or else the window will still be held in memory.) If the containing object is destroyed manually, the events will also be destroyed.

Categories

Resources