Re: Delegates and Events confusion

Tech Tip: Click here to run a free scan for Windows Errors and optimize PC performance

From: J.Marsch (jeremy_at_ctcdeveloper.com)
Date: 06/17/04


Date: Thu, 17 Jun 2004 11:41:46 -0500

The pattern does not require that you implement Finalize and IDisposeable in
tandem. It merely advises that you do so. In this case, implementing a
finalize is unneccessary, and not even fruitful. You generally implement a
finalizer if your object holds unmanaged resources that must be
deterministically released when your managed object goes away. You
complement that finalizer with a Dispose so that the developer can call
dispose and bypass an expensive finalization. Here, we have a completely
different situation.

In the case in this thread, a finalizer would never be called, because if
you don't deterministically dispose this object (and unsubscribe the events)
it is _never eligible_ for collection (or finalization). Well, to be more
accurate: finalize would be called, but only when the event publisher is
elligible for collection. By that time, 1. The subscriber has lived too
long, and 2. there's no use in unsubscribing the events because the event
publisher is already gone.

Rather, by calling Dispose, you are allowing the object to become eligible
for collection. Again, if the object doesn't release its event
subscriptions, it will not even be eligible for collection until the event
publisher is eligible.

You could name the method something else (ReleaseEvents, or whatever), but
it's still fulfilling the same purpose as a Dispose. By naming it Dispose()
and implementing IDisposeable, you do two things:

1. You draw attention to other developers that this is a Disposeable object
( a known pattern), and so (hopefully) increase the likely hood that other
developers will call Dispose on the object
2. By implementing IDisposeable, you make the object compatible with the
Using() statement for automatic disposal when you are finished with it.

"Angelos Petropoulos" <Spam.Me@This.Address.Not> wrote in message
news:iul2d09ou44j7qtthv2iote0u9qom5en7m@4ax.com...
> No no no no no no, this is no reason to implement the IDisposable
> interface!
>
> When you implement the IDisposable interface, the design pattern
> requires you to implement Finalize as well. The code inside Finalize
> calls the Dispose method (it is in other words your failsafe in case
> the client does not call Dispose). If you call Dispose manually,
> GC.SuppressFinalization is called and the object is not placed in the
> Finalization queue.
>
> Now, not only do you not want to get into all this for every class
> that subscribes to events, but implementing the Finalize method means
> that every instance of that class is placed in the finalization list
> of the GC so that it knows not to free up the memory that the object
> is occupying before calling its Finalize method. Even if you do call
> Dipose manually and you suppress the object's finalization, the
> initial cost of placing the object in the finalization list is still
> there.
>
> The IDisposable interface should only be implemented when you are
> dealing with unmanged resources, which if not freed would cause a
> memory leak.
>
> Regards,
> Angelos Petropoulos
>
> On Tue, 15 Jun 2004 18:21:05 -0500, "J.Marsch"
> <jeremy@ctcdeveloper.com> wrote:
>
> >Are you unsubscribing the event when you remove the instances of Class C
> >from memory?
> >
> >If you subscribe to an event and do not unsubscribe, the subscriber will
be
> >held in memory until the event publisher (class A) is collected.
> >
> >Here's why:
> >A delegate has a member called Target. Target is a reference to the
> >subscriber (Class C). When you add the event, the publisher holds a
> >reference to the delegate which in turn holds a reference to the
subscriber,
> >so C is held in memory. The graph looks like this:
> >
> >A ---> EventDelegate ---->C
> >
> >So, the reason that you are getting multiple event fires is that you have
> >more instances of C in memory than you think that you do.
> >
> >What you probably want to do is to keep track of all of the event
> >subscriptions that C makes and have C implement IDispose. When you are
> >ready to kill C, call its Dispose method. Inside the Dispose,
unsubscribe
> >from all of the events that you are subscribed to.
> >
> >"Rhy Mednick" <rhy@rhy.com> wrote in message
> >news:uVScA1yUEHA.584@TK2MSFTNGP09.phx.gbl...
> >> I have a class (let's call it ClassA) that I've written which has
events.
> >> In another class (let's call it ClassB) I create a collection of ClassA
> >> objects. In a third class (ClassC) I create a reference to some of the
> >> ClassA objects created in ClassB. In ClassC I hook into the ClassA
events
> >> with a foreach loop so that I hook each object. The code is something
> >like
> >> this:
> >> class ClassC {
> >> void SomeMethod()
> >> {
> >> foreach (ClassA item in ClassACollection)
> >> {
> >> item.MyEvent += new EventHandler(item_MyEvent);
> >> }
> >> }
> >> }
> >>
> >> Objects of type ClassC keep getting created and deleted, but the
objects
> >of
> >> ClassA that it references stay in memory. Therefore, every time I load
a
> >> new instance of ClassC the event gets hooked again. What I'm seeing is
> >that
> >> I fire the event once but I end up getting it raised in the code that
> >> responds to it (item_MyEvent method) for every time it was added when I
> >only
> >> want it to get caught once.
> >>
> >> How can I be sure that I only add the handler to the event once for
each
> >> item? Since the code is in ClassC it's not allowed to inspect the
> >> ClassA.MyEvent to see if something's already hooked to it.
> >>
> >> I hope this makes sense. Creating a repro case wouldn't make sense
here
> >> because it's my application logic that's causing the problem.
> >>
> >> Thanks.
> >>
> >>
> >
>



Relevant Pages

  • Re: A re-announce on GCs defects
    ... Finalize() as soon as possible if it's been overridden, ... IDispose, or the Disposemethod. ... finalizers on finalizable objects (objects overriding the Finalize() ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Memory Leak Experts!!!!
    ... you also implement the Finalize method which simply calles the Dispose ... >interface implementation has interfered with the normal function of the GC. ... >Deterministic disposing or implementing the IDispose pattern is only a ...
    (microsoft.public.dotnet.framework.performance)
  • Re: destructors
    ... > not run with the addition of constrained execution regions. ... >>> They are the same thing as the Finalize() function. ... >>> This is why the IDispose Interface was created. ... you just call Dispose(). ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Automatic Dispose
    ... But, a SqlConnection itselft is a managed resource and, as such, should not ... instance user uses "Using" and Dispose is called automatically). ... Public Overridable Sub Dispose() Implements IDisposable.Dispose ... Protected Overrides Sub Finalize() ...
    (microsoft.public.dotnet.languages.vb)
  • Re: Automatic Dispose
    ... instance user uses "Using" and Dispose is called automatically). ... Public Overridable Sub Dispose() Implements IDisposable.Dispose ... Protected Overrides Sub Finalize() ... Private con As SqlClient.SqlConnection ' managed resource this class ...
    (microsoft.public.dotnet.languages.vb)