How to use delegates properly? (using WPF Storyboards as example) - c#

I have the following code in a Windows Phone 7 Silverlight App of mine which is executed before navigating to another page.
Storyboard storyboard = Resources["TurnstileBackwardOut"] as Storyboard;
Storyboard.SetTarget(storyboard, LayoutRoot);
storyboard.Completed += delegate
{
storyboard.Stop();
Debug.WriteLine("LeavePageStoryboard.Completed");
NavigationService.Navigate(uri);
};
storyboard.Begin();
The Storyboard is in the resources and is reused quite often. As I understand it, whenever the code is executed, a new delegate is added to the Completed event. My question is: do I have to remove that delegate from the Completed event?
Otherwise after some time, there would be many delegates and some of them wouldn't even belong to the current page, because I use the storyboard on other pages as well.

You should be able to tell if this is a problem, because when you navigate you'll see lots of copies of "LeavePageStoryboard.Completed". Unless it's creating a new Storyboard each time, however, I suspect it is a problem. It's easy to fix though:
EventHandler completedHandler = null; // For definite assignment purposes
completedHandler = delegate
{
storyboard.Stop();
Debug.WriteLine("LeavePageStoryboard.Completed");
NavigationService.Navigate(uri);
storyboard.Completed -= completedHandler;
};
storyboard.Completed += completedHandler;

This is a memory leak, and a common one in C#. If you have a long lived object that gets reused often, it's common for event handlers to pile up on it.
There are a couple options:
1) Make your delegate not be anonymous. Create a method to hold its logic, and referenced delegates. Then remove this referenced delegate when you are done with storyboard.
2) Make storyboard not be long lived. One possibility is to make it a DataTemplate instead and instantiate new copies of it. Then you can attach event handlers to it all you want and once garbage collected, they will get garbage collected too.

Related

Event handler is removed by GC when subscribed to external event

For work, I have to subscribe to an external event and handle it when it comes by. I have no problem doing all that but I noticed that after the first GC pass, the event handler I added seemingly gets removed. I won't be able to receive the next event after the collection.
If I manually trigger the GC then remove and add the handler, I will properly receive the next event.
I tried using KeepAlive and SuppressFinalize from the GC to no avail. I also tried to add the handler as a lambda, but it still gets removed after the first garbage collection.
example code (pseudo, for reference only)
private externalLibrary.Server server = new Server();
private externalLibrary.DataHandler handler; //delegate is assigned in constructor
public bool Subscribe(){
//...
server.Data.Received += handler;
}
With this setup, I receive events until the first GC.
My ugly workaround looks like this:
ResetHandler(){
server.Data.Received -= handler;//It seems to already be removed by GC but better be safe than sorry
server.Data.Received += handler;
}
And in the code when I finished my processing of the event, I do:
GC.Collect;
ResetHandler();
This allows me to circumvent the problem. I would like to find a better solution or know what I am doing wrong.
Please note that this is my very first question here.
Thank you for your input.

Eventhandlers and application lifetime objects in c# - should I unhook?

Say I've got an object that exists for the duration of an application. Let's call it GlobalWorker. GlobalWorker has events e.g. UserLoggedIn to which other objects can subscribe. Now imagine I've got a Silverlight page or an asp.net page or something, that subscribes to the UserLoggedIn event when it is constructed.
public SomePage()
{
GlobalWorker.Instance.UserLoggedIn += new EventHandler(OnUserLoggedIn);
}
private void OnLoggedIn(object sender, EventArgs e)
{
}
Does the existence of this event prevent the page from being garbage collected? If so, what's the best pattern to use in this instance: weak event-handlers, move the subscription to the Load event and unsubscribe in the UnLoad event?
Use Weak Events.
This is a common problem in WPF and it is good you have thought about it.
Yes the behavior prevents the page from being GC.
The reason being is that UserLoggedIn will hold a reference to SomePage indefinitely. There is no explicit removal of the handler and since weak events are not being used it will not implicitly get removed either.
You can use weak events as another poster stated, you can also re-think your design to some degree and see if you can functionalize or encapsulate the eventing behavior. Perhaps the data is all that needs to be global in this instance (user credentials), where as the event can be kept isolated.
You could also de-register in the handler itself if this was a one-off event you cared about. It really boils down to your specific need and instance, weak event pattern is the pattern to deal with this application wide but does not mean you have to use that pattern in each and every instance this problem surfaces.

Should I unsubscribe from events? [duplicate]

This question already has answers here:
Is it bad to not unregister event handlers?
(2 answers)
Closed 9 years ago.
I have 3 questions concerning events:
Should I always unsubscribe events that were subscribed?
What happens if I do NOT?
In the below examples, how would you unsubscribe from the subscribed events?
I have for example this code:
Ctor: Purpose: For database property updates
this.PropertyChanged += (o, e) =>
{
switch (e.PropertyName)
{
case "FirstName": break;
case "LastName": break;
}
};
and this: Purpose: For GUI-binding wrap the model into viewmodels
ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate);
PeriodListViewModel = new ObservableCollection<PeriodViewModel>();
foreach (Period period in periods)
{
PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo);
foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList)
{
documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument);
documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
}
PeriodListViewModel.Add(periodViewModel);
}
Well, let's take the last question first. You can't reliably unsubscribe from an event that you've subscribed to directly with a lambda expression. You either need to keep a variable around with the delegate in (so you can still use a lambda expression) or you need to use a method group conversion instead.
Now as for whether you actually need to unsubscribe, it depends on the relationship between the event producer and the event consumer. If the event producer should live for longer than the event consumer, you should unsubscribe - because otherwise the producer will have a reference to the consumer, keeping it alive for longer than it should be. The event handler will also keep getting called for as long as the producer produces it.
Now in many cases that's not a problem - for example, in a form the button that raises the Click event is likely to live for about as long as the form on which it's created, where the handler is typically subscribed... so there's no need to unsubscribe. This is very typical for a GUI.
Likewise if you create a WebClient solely for the purpose of a single asynchronous request, subscribe to the relevant event and start the asynchronous request, then the WebClient itself will be eligible for garbage collection when the request has finished (assuming you don't keep a reference elsewhere).
Basically, you should always consider the relationship between the producer and the consumer. If the producer is going to live longer than you want the consumer to, or it's going to continue raising the event after you're no longer interested in it, then you should unsubscribe.
1) It depends. Usually it's a good idea, but there are typical cases where you don't need to. Basically, if you are sure that the subscribing object is going to outlive the event source, you ought to unsubscribe, otherwise this would create an unnecessary reference.
If however your object is subscribing to its own events, like in the following:
<Window Loaded="self_Loaded" ...>...</Window>
--then you don't have to.
2) Subscribing to an event makes additional reference to the subscribing object. So if you don't unsubscribe, your object might be kept alive by this reference, making effectively a memory leak. By unsubscribing you are removing that reference. Note that in the case of self-subscription the problem doesn't arise.
3) You can do like that:
this.PropertyChanged += PropertyChangedHandler;
...
this.PropertyChanged -= PropertyChangedHandler;
where
void PropertyChangedHandler(object o, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "FirstName": break;
case "LastName": break;
}
}
You don't have to desubscribe from an event when the instance that is subscribing has the same scope as the instance that is being subscribed to.
Lets say you are a form and you are subscribing to a control, these two together form a group. However, if you have a central class that manages forms and you have subscribed to the Closed event of that form, these do not form a group together and you must desubscribe once the form is closed.
Subscribing to an event makes the subscribed instance create a reference to the instance being subscribed to. This prevents garbage collection. So, when you have a central class that manages form instances, this will keep all forms in memory.
WPF is an exception because it has a weak event model where events are subscribed to using weak references and it will not hold the form in memory. However, it is still best practice to desubscribe when you are not part of the form.
You may take a look at this article on MSDN. Quote:
To prevent your event handler from
being invoked when the event is
raised, simply unsubscribe from the
event. In order to prevent resource
leaks, it is important to unsubscribe
from events before you dispose of a
subscriber object. 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.
1.) Should I always desubscribe events that were subscribed?
Usually yes. The only exception is when the object on which you subscribed isn't referenced anymore and will be garbage collected soon.
2.) What happens if I do NOT?
The object on which you subscribed will hold a reference to the delegate, which in turn holds a reference to its this pointer, and thus you'll get a memory leak.
Or if the handler was a lamda it will hold onto whatever local variables it bound, which thus won't be collected either.

What's keeping this timer in scope? The anonymous method?

Ok,
So I have a method which fires when someone clicks on our Icon in a silverlight application, seen below:
private void Logo_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
ShowInfo(true);
DispatcherTimer autoCloseTimer = new DispatcherTimer();
autoCloseTimer.Interval = new TimeSpan(0, 0, 10);
autoCloseTimer.Tick +=new EventHandler((timerSender,args) =>
{
autoCloseTimer.Stop();
ShowInfo(false);
});
autoCloseTimer.Start();
}
Whats meant to happen is that the method ShowInfo() opens up a box with the company info in and the dispatch timer auto closes it after said timespan. And this all works...
But what I'm not sure about is because the dispatch timer is a local var, after the Logo_MouseLeftButtonUp method finishes, what is there to keep the dispatch timer referenced and not availible for GC collection before the anonymous method is fired?
Is it the reference to the ShowInfo() method in the anonymous method?
Just feels like some thing I should understand deeper as I can imagine with using events etc it can be very easy to create a leak with something like this.
Hope this all makes sense!
Andy.
The DispatcherTimer registers itself with the Dispatcher by calling the internal Dispatcher.AddTimer method when you call Start.
Since it also unregisters itself by calling Dispatcher.RemoveTimer when you call Stop, you won't leak memory.
The Timer keeps the anonymous method alive in its Tick event, which also keeps the variables in the method alive through the closure.
One of the more obscure causes of memory leaks in .NET is event handlers. An event handler is a reference to an object, and keeps the object in scope. When you're done with an event handler, it needs to be dropped. If the event handler is used only once, it can deregister itself, but if it gets used more than once, there will need to be some other object that knows when it's no longer useful, and drops the event handler.

Event subscription to a static instance. How to ensure GC

I'm fixing a User control component using the .NET 1.1 framework.
There are many instances of this user control referencing a singleton wrapping a COM resource.
They subscribe to events from this resource.
I suspect that the reason why we are having a degrading performance is because the singleton is maintaining a reference to the user control classes even after their respective windows are gone. Thus preventing GC.
They unsubscribe the event in their finalize method.
Hence I suspect a chicken and egg problem. The finalize wont execute because they are being referenced through their event subscription in the longer lived Singleton, preventing GC.
Where in a User Control should I perform the event unsubscribe to make it eligible for GC?
I do not own the application hosting the user control.
You should unsubscribe when the control is disposed, really. If you can't easily modify the Dispose method yourself, you could add an event handler to the Disposed event:
ComSingleton.Foo += FooHandler;
this.Disposed += delegate { ComSingleton.Foo -= FooHandler; };
If you want to subscribe to events using anonymous methods or lambda expressions, you'll need to use a separate local variable so that you can refer to it again:
EventHandler tmp = delegate { Console.WriteLine("Something happened"); };
ComSingleton.Foo += tmp;
this.Disposed += delegate { ComSingleton.Foo -= tmp; };
An alternative is to use "weak event handlers" - there are many articles about this on the web; here's one which goes into a fair amount of detail.

Categories