Where to unsubscribe to events in a form that uses an external object.

  • (2 Pages)
  • +
  • 1
  • 2

19 Replies - 3073 Views - Last Post: 26 September 2011 - 07:32 AM Rate Topic: -----

#1 CodeGuru1   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 01-October 10

Where to unsubscribe to events in a form that uses an external object.

Posted 24 September 2011 - 05:29 PM

Lets say I built a form that takes some object as a parameter of the constructor, and subscribes to an event of that object in the constructor.

Where would I unsubscribe to this event to prevent leaks? I can't seem to override dispose to put the unsubscribers in there where I think they should go?

public class RaiseEvents
{
	public event Action<string> ListenToMe;
}

public partial class FormThatDisplaysEventInfo : frmBase
{
	public FormThatDisplaysEventInfo(RaiseEvents eventRaiser)
	{
		InitializeComponent();

		eventRaiser.ListenToMe += new Action<string>(eventRaiser_ListenToMe);
	}
}



Thanks!

This post has been edited by CodeGuru1: 24 September 2011 - 05:33 PM


Is This A Good Question/Topic? 0
  • +

Replies To: Where to unsubscribe to events in a form that uses an external object.

#2 [email protected]   User is offline

  • JMP *0x0(%RIP)
  • member icon

Reputation: 37
  • View blog
  • Posts: 1,019
  • Joined: 20-February 09

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 24 September 2011 - 06:06 PM

i would add the
eventRaiser.ListenToMe += new Action<string>(eventRaiser_ListenToMe);


in the forms onClosing Method.but that requires another event but iirc that's taken care of automatically by the GC
so what i would do is
public class RaiseEvents
{
	public event Action<string> ListenToMe;
}

public partial class FormThatDisplaysEventInfo : frmBase
{
	public FormThatDisplaysEventInfo(RaiseEvents eventRaiser)
	{
		InitializeComponent();

		eventRaiser.ListenToMe += new Action<string>(eventRaiser_ListenToMe);
this.onClosing += new ...(() => { eventRaiser.ListenToMe -= new Action<string>(eventRaiser_ListenToMe);});
	}
}


it may or may not work. but thats what i would do.
Was This Post Helpful? 1
  • +
  • -

#3 CodeGuru1   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 01-October 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 24 September 2011 - 06:42 PM

Ok, I can see where I was confused.

I didn't realize FormClosing meant that the form object was also being disposed, per MSDN: http://msdn.microsof...ormclosing.aspx

So ya, I guess I'll just subscribe to FormClosing and unsubscribe from events in there.

I like your method. Very clean.

Now I'm thinking though that I should use FormClosed event and not FormClosing. What if another FormClosing function of my form object sets e.cancel to true....

This post has been edited by CodeGuru1: 24 September 2011 - 06:52 PM

Was This Post Helpful? 0
  • +
  • -

#4 [email protected]   User is offline

  • JMP *0x0(%RIP)
  • member icon

Reputation: 37
  • View blog
  • Posts: 1,019
  • Joined: 20-February 09

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 24 September 2011 - 08:08 PM

Well thats entirely up to what you need it to do. and if it sets e.cancel then it will stop the calling :/ whay you could do is add a condition to check e.cancel and if its true then dont unreg the event.
Was This Post Helpful? 0
  • +
  • -

#5 CodeGuru1   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 01-October 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 24 September 2011 - 08:26 PM

Right, but what I mean is that someone could derive from my form, or subscribe to FormClosing elsewhere in the code and set e.cancel to true.

If the FormClosing event that unsubscribes the foreign object hook gets called first, and another FormClosing event cancels the closing event, it would lead to a broken form.

Which suggests that I should use FormClosed or Disposed events to unsubscribe to events hooked in the constructor.

ps. chick in your sig is hot.

This post has been edited by CodeGuru1: 24 September 2011 - 08:26 PM

Was This Post Helpful? 0
  • +
  • -

#6 [email protected]   User is offline

  • JMP *0x0(%RIP)
  • member icon

Reputation: 37
  • View blog
  • Posts: 1,019
  • Joined: 20-February 09

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 24 September 2011 - 08:31 PM

Yeah, you would hook it into the this.Closed Event then. that should work, as for the sig, yeah vary ^_^

This post has been edited by [email protected]: 24 September 2011 - 08:31 PM

Was This Post Helpful? 0
  • +
  • -

#7 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6535
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 05:42 AM

Let's not forget about destructors for forms and classes.
They are run as the last thing.

Form_Closing
Form_Closed
Then from is destructed

If you unsubscribe in the destructor you know it will only be run after the form is fully closed, off the radar of all other classes and about to be disposed of.


class myForm : form
{
     public myForm()
     {
        // This is the constructor
     }

     ~myForm()
     {
        // This is the destructor
        // Runs last, after the form is closed.
     }
}

Was This Post Helpful? 0
  • +
  • -

#8 CodeGuru1   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 01-October 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 07:17 AM

Hmm.. I've been doing a lot of reading lately to improve my C# and recall that Destructors should only be used to clean up unmanaged resources.
Was This Post Helpful? 0
  • +
  • -

#9 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6535
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 07:52 AM

In my way of looking it at, if the subscriptions were properly managed, they wouldn't have to be manually unsubscribed.

Anything I have to track and manage... count all my subscriptions and unsubscribe as many times as I subscribed... If I have to manage it, then it is not managed by the framework.

As you said yourself, this is to avoid a memory leak. Therefore somewhere, buried deep in the bowels of all this - its not managed if it can create a memory leak.
Was This Post Helpful? 0
  • +
  • -

#10 CodeGuru1   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 29
  • Joined: 01-October 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 09:21 AM

Thanks t. I have many of your posts/threads favorited btw.

I guess I don't understand what the proper design pattern should be for a form that simply listens to the events of an object.

This seems to me like a pretty simple and common practice but I can't seem to find any examples on suggested design.

When you say "if the subscriptions were properly managed", are you suggesting that in my example they are not properly managed? Am I doing something less than desirable?

When I say this is to avoid a memory leak, I know that if I subscribe to eventRaiser events in my FormThatDisplaysEventInfo, eventRaiser will maintain a handle to FormThatDisplaysEventInfo, and thus the Form cannot be freed.

Sure, I could just not unsubscribe from the events and check inside of the event receiver code in the Form for the Form.IsDisposed and unsubscribe, but that would only remove all references to the form _IF_ the event is actually called.

I'm confused...
Was This Post Helpful? 0
  • +
  • -

#11 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6535
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 11:15 AM

Quote

When you say "if the subscriptions were properly managed", are you suggesting that in my example they are not properly managed? Am I doing something less than desirable?
No. I'm suggesting that the .NET framework is lacking in its management of event subscriptions. Its just like how a pen or brush is not properly managed because it is really just a .NET framework wrapper around an unmanaged object. Leaving the developer having to manage these .NET objects' lifespans and manually dispose of them when done.

Quote

When I say this is to avoid a memory leak, I know that if I subscribe to eventRaiser events in my FormThatDisplaysEventInfo, eventRaiser will maintain a handle to FormThatDisplaysEventInfo, and thus the Form cannot be freed.
Actually the form can be disposed of if that's what you mean by 'freed'. And therein lies the problem. You *can* dispose of the form without the form or framework unsubscribing to all the subscriptions that you created programmatically. Keep in mind that there are a lot of subscriptions that you don't handle that the form does for you. If you drag a button, double click it so Visual Studio does the subscription to it's click event - that is disposed of automatically when the form is disposed of. We're really only talking about unsubscribing to things you programmatically added in your .cs file. Since you made the subscription, you need to make the unsubscription.

Quote

I guess I don't understand what the proper design pattern should be for a form that simply listens to the events of an object.
When you are done with something (pen or event or resource), handle it then and there and you won't have to worry about it. For me I often do it in the .Dispose() method or destructor, do your unsubscriptions. That's what works for me.

I tend to make a CreateWidget() method and a DestroyWidget() method. In creating a thing I subscribe. In the Destroy method I unsubscribe. First I deal with all my subscriptions and have a mass of line with += . When I have all that working and am happy I copy/past those lines into the Destroy method. Then I find/replay all the += with -= in that selection.

But I usually break that down with Subscribe() and Unsubscribe() methods. Creating a thing should not be the only way to subscribe and disposing should not be the only way to unsubscribe. This is in keeping that each method should have only one purpose in life and not have side affects. It makes it easy to change the sequence of workflow, call a single-purpose method from a button.Click and so on.

class Widget()
{
    public Widget()
    {
        // Constructor
        InitializeComponents();
        LoadUserSettings();
        SetUpGUI();
        DoSomeOtherThing();
        SubscribeToEvents();
    }

    public void Dispose()
    {
       If (dirty) SaveUserSettings();
       DoSomeOtherHousekeeping();
       UnsubscribeFromEvents();
    }
}


It sort of comes to style at this point. Ask 10 coders and you'll get 10 different answers, and they all work. Some do it at .Dispose(). Some do it at ~Widget() destructor. Some make Subscribe() and UnSubscribe() methods. Some project just require a mix of all these styles because that is simply the nature of that project.

I have some old projects where I do an unsubscribe at the top of a property setter... do some work that I don't want to raise more events... then resubscribe after I've changed something.

private decimal SomeValue(value)
{
   set
     {
        _somevalue = value;
        if (nudSomeValue.Value != value)
        {
           nudSomeValue.ValueChanged -= SomeValueChangedHandler();
           LogThis -= LogThisHandler();
           DisplayThis -= DisplayThisHandler();
           // Do some work 
           // Then resubscribe
           // avoids a circular event change of change, react, change, {repeat}
           nudSomeValue.ValueChanged += SomeValueChangedHandler();
           LogThis += LogThisHandler();
           DisplayThis += DisplayThisHandler();
         }
      }
}


I'm not saying that's ideal or right or perfect. But sometimes as much as we try, we find ourselves making compromises to our professional integrity in order to meet deadlines or not completely re-right some other guy's method.
Was This Post Helpful? 1
  • +
  • -

#12 [email protected]   User is offline

  • D.I.C Addict
  • member icon

Reputation: 1003
  • View blog
  • Posts: 975
  • Joined: 30-September 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 11:29 AM

You should also take a look at Weak Events which address this problem with events using weak references (as opposed to the standard strong references, of which cause the problem). That is actually a formal pattern, so it may help give you some solid direction with regards to this subject. Here is Microsoft's documentation on the pattern.

You could put the unsubscriptions in a Dispose() method of the form, which seems like a very reasonable approach.

In the case of your form example, I think unsubscribing from the events in the FormClosed event handler method is a reasonable way to go about it. If the form gets closed, all events will be unsubscribed, so the form will be able to become eligible for garbage collection. All good!


I also echo tlhIn`toq's point about using dedicated Subscribe() and Unsubscribe() methods. I do that too.


tlhIn`toq, that technique of using the destructor to unsubscribe from events has always confused me slightly. It could be because I am not correctly understanding how the GC does its work in some way, but...

The problem with normal .NET events is obviously that the source object holds onto a reference to the listener object, so if the source lives longer than the listener, the listener will be kept in memory unnecessarily as the source object is holding a strong reference to it, meaning it will not be eligible for garbage collection.

Further, by adding a destructor to the listener object to unsubscribe from the event, the object gets added to the finalization list when it is created. When a garbage collection happens, objects in the finalization list that have no strong references to them (and are therefore considered garbage), are appended to the freachable queue, where they get their destructors called.

So, my confusion is, how does an listener object with a destructor get from the finalization list to the freachable queue, if the source object has a strong reference to the listener object. It is my understanding therefore that the destructor will never be called unless the listener object becomes eligible for garbage collection, and it won't become eligible for garbage collection until the events are unsubscribed.

It's always seemed a sort of catch 22 situation from my understanding of how finalization works :)/>/>

This post has been edited by [email protected]: 24 December 2012 - 10:23 AM

Was This Post Helpful? 2
  • +
  • -

#13 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6535
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 11:43 AM

Sounds like you know it better than I. I am be wrong in thinking that those objects are actually being destroyed/unsubscribed. I can't point to an actual project I've made in the last few years where I do that, but I know I have in the past. Maybe I outgrew doing it that way without ever realizing it didn't work. Maybe that's *why* I outgrew it and just don't recall the specifics of that change to my style/practices.
Was This Post Helpful? 0
  • +
  • -

#14 [email protected]   User is offline

  • D.I.C Addict
  • member icon

Reputation: 1003
  • View blog
  • Posts: 975
  • Joined: 30-September 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 12:28 PM

Oh right. That's okay then. It's just I have seen that technique used quite often, and I thought I was missing something :). I don't like feeling like I'm missing anything ;)

Quote

Keep in mind that there are a lot of subscriptions that you don't handle that the form does for you. If you drag a button, double click it so Visual Studio does the subscription to it's click event - that is disposed of automatically when the form is disposed of.


Also, I don't think the unsubscriptions are done automatically for you in that case. Afterall, using the designer to create the event subscriptions is not any different from manually subscribing yourself (the same code is produced).

I think GUI control events are left subscribed (unless we explicitly unsubscribe them). However, this isn't really a problem as the source (say the button) will live as long as the listener (the parent form), and not really any longer. Therefore, there isn't likely to be a 'memory leak' in that situation.

That does make a very important point though:

Generally, only worry about 'memory leaks' arising from event subscriptions if the listener object is likely to live longer than the source object. If that is not the case, you are generally okay to leave events subscribed
Was This Post Helpful? 0
  • +
  • -

#15 tlhIn`toq   User is offline

  • Xamarin Cert. Dev.
  • member icon

Reputation: 6535
  • View blog
  • Posts: 14,450
  • Joined: 02-June 10

Re: Where to unsubscribe to events in a form that uses an external object.

Posted 25 September 2011 - 01:32 PM

View Post[email protected], on 25 September 2011 - 01:28 PM, said:

I think GUI control events are left subscribed (unless we explicitly unsubscribe them). However, this isn't really a problem as the source (say the button) will live as long as the listener (the parent form), and not really any longer. Therefore, there isn't likely to be a 'memory leak' in that situation.


I do wonder about this and might make a test app to imperically test it.

Picture the situation where you make a new form for a new Contact entry. When done you save the contact and close the form. There had to be designer-created event subscriptions in there.
If it really doesn't unsubscribe and there are 5 events left unsubscribed its a petty amount of 'leaked' memory. But if you left the application open long enough and entered 100,000 contacts without restarting how much memory would be lost to unsubscribed "Done" button events?
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2