Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

Can I have back my destructors, please?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
cctv.star@gmail.com - 21 Feb 2008 01:42 GMT
In C++ you have RAII idiom that takes care of all your resources -
memory, file handles, etc.

In C# you have GC that takes care of the memory, but it looks like
you're completely on you own as far as other resources are concerned -
just miss this Dispose() call and wait for all kind of interesting
things to happen.

For every class that you consume the first thing you should note is
whether it implements IDisposable or not, and handle it accordingly.

Now the problem: suppose you use some class, which does not implement
IDisposable. All is well. And then, the maintainer of this class
decides that he needs to use some other object, which does implement
IDisposable, and to make this object a member. Although this should be
just an implementation detail and completely private business, he has
to make his own class implement IDisposable.
And now your code still compiles fine, but is no longer correct,
although the only thing that really has changed is just implementation
of a class you use.

In a sense,  whether some class implements IDisposable or not is just
an implementation artifact for the author, but is indeed an interface
change for the consumer.

Actually, I'm still learning C#, so I hope something of what I've
written above is incorrect. Otherwise, please advise how to deal with
the problem I've mentioned in practice (is there any way, at least, to
make the consumer code uncompilable after such change?).
Arne Vajhøj - 21 Feb 2008 02:15 GMT
> In C++ you have RAII idiom that takes care of all your resources -
> memory, file handles, etc.
[quoted text clipped - 25 lines]
> the problem I've mentioned in practice (is there any way, at least, to
> make the consumer code uncompilable after such change?).

Question in subject line: no.

Last question: not really. The best direction would be
to layer the code, so that each layer expose a clean
interface with no such requirements. The problem happen
because the two classes you consider independent of each
other are in fact tightly coupled.

Arne
Scott Roberts - 21 Feb 2008 03:28 GMT
>> In C++ you have RAII idiom that takes care of all your resources -
>> memory, file handles, etc.
[quoted text clipped - 35 lines]
>
> Arne

I also have trouble with IDisposable. Not the concept, just with remember
if/when I need to "dispose"or not.

Could you expand on your suggestion to "layer the code"? For example, I
don't feel that using the SqlConnection class from within my class
necessarily makes my class "tightly coupled" with the SqlConnection class.
However, my code does need to know whether SqlConnection implements
IDisposable or not and act accordingly. How could I "layer" my code to
remove this "coupling"? Or am I misunderstanding you completely?
Marc Gravell - 21 Feb 2008 05:12 GMT
> but it looks like
> you're completely on you own as far as other resources are concerned
You still have the finalizer, which gets invoked as GC destroys an
object... but this should only be used as a safety net (and ideally
would whinge: ideally IDisposable should be used)

> For example, I
> don't feel that using the SqlConnection class from within my class
> necessarily makes my class "tightly coupled" with the SqlConnection class.

If you mean "within some method(s), obtains, uses and releases a
SqlConnection" then I'd agree; if you mean "has a SqlConnection as an
instance-field that only got created because of this instance [perhaps
in the constructor]", then I'd argue that this very much *does* couple
them, and your class should implement IDisposable to hopefull clean up
the connection ASAP. Of course, perhaps a better pattern here would be
to not have a SqlConnection as a field, and let the connection-pool do
its intended job - but that is very example specific.

> if/when I need to "dispose"or not.

True, true; well a good rule of thumb is to (at least) check for a
public Dispose() method, and wrap with "using". But the IDE could help
out here! I can't remember, but maybe FxCop has a rule on this?

Marc
KWienhold - 21 Feb 2008 07:56 GMT
> > if/when I need to "dispose"or not.
>
[quoted text clipped - 3 lines]
>
> Marc

I agree, I have always wondered why VS doesn't tell me that I have
created an instance of a class that implements IDisposable, but forgot
to call Dispose() on it.
However that wouldn't really help if (like the OP mentions) a class
you are already using decides to implement IDisposable without you
knowing about it.
Then again, implementing IDisposable is definately a breaking change,
so it should not be made once an interface has become public, so
hopefully this should not happen.

Kevin Wienhold
Peter Duniho - 21 Feb 2008 18:54 GMT
>> > if/when I need to "dispose"or not.
>>
[quoted text clipped - 7 lines]
> created an instance of a class that implements IDisposable, but forgot
> to call Dispose() on it.

Well, to be fair, it's not an easy problem to solve.  Since Dispose() can  
legitimately be called anywhere, not just in the local scope where the  
object is instantiated, or even within the same assembly for that matter,  
there could be a lot of false positives on such a warning.

But I do agree (and have said so here before :) ) that _something_ ought  
to be done somewhere to advertise better in the IDE that you're using an  
object that implements IDisposable and thus needs disposing at some point.

I just don't know off the top of my head what the best way to do that  
would be.  :)

> However that wouldn't really help if (like the OP mentions) a class
> you are already using decides to implement IDisposable without you
> knowing about it.
> Then again, implementing IDisposable is definately a breaking change,
> so it should not be made once an interface has become public, so
> hopefully this should not happen.

Yes, I wouldn't worry about that case too much.  It'd be a very bad idea  
to add IDisposable to an object after the fact without going back and  
revisiting every single use of that object.

Pete
JS - 05 Mar 2008 19:03 GMT
> I just don't know off the top of my head what the best way to do that  
> would be.  :)

At the very least, it should be more obvious when you're working with
an IDisposable object.  Maybe the editor could highlight IDisposable
types with a different color, or intellisense could somehow show you
that the object is IDisposable.
cctv.star@gmail.com - 21 Feb 2008 20:08 GMT
> Then again, implementing IDisposable is definately a breaking change,
> so it should not be made once an interface has become public, so
> hopefully this should not happen.

The problem is, implementing IDisposable could be caused by
implementation needs.

I can't help thinking about IDisposable as having very odd semantics,
different to that of other, "normal" interfaces - it doesn't provide
any service to a client, but rather makes the client responsible (for
calling Dispose()). And since necessity for cleanup is an
implementation artifact rather than a real public interface change, I
can easily imagine it coming during later stages of development.
Jeroen Mostert - 21 Feb 2008 20:25 GMT
>> Then again, implementing IDisposable is definately a breaking change,
>> so it should not be made once an interface has become public, so
[quoted text clipped - 8 lines]
> calling Dispose()). And since necessity for cleanup is an
> implementation artifact rather than a real public interface change,

See, that's where you're wrong, because you're used to C++. Because *all*
finalization is deterministic in C++, it does reduce to an implementation
artifact there. But exactly because .NET has nondeterministic finalization,
giving the class a need for deterministic finalization is a breaking change.
The public interface remains the same, but the semantics are different.

It's not merely "an implementation artifact", because it makes a tangible
difference to the users of the class: if they don't Dispose() explicitly,
there's a chance for resource leaks. Transient resource leaks, granted, but
that's still enough to cripple your application. That sort of "artifact"
deserves all the attention it can get.

> I can easily imagine it coming during later stages of development.

Yes, and that's unfortunate. On the other hand, it's not so common for a
class to "suddenly" start owning precious resources that require disposing
without anyone ever foreseeing the need for this. Especially not if those
resources are claimed during the object's entire lifetime. If it can
deterministically dispose of a field sooner than that, it should definitely
do so.

Signature

J.

cctv.star@gmail.com - 21 Feb 2008 23:58 GMT
> See, that's where you're wrong, because you're used to C++. Because *all*
> finalization is deterministic in C++, it does reduce to an implementation
> artifact there. But exactly because .NET has nondeterministic finalization,
> giving the class a need for deterministic finalization is a breaking change.
> The public interface remains the same, but the semantics are different.

Yes, that I think was the root of my confusion.

Thanks for the good explanation!
Ollie - 21 Feb 2008 11:07 GMT
> In C++ you have RAII idiom that takes care of all your resources -
> memory, file handles, etc.
[quoted text clipped - 25 lines]
> the problem I've mentioned in practice (is there any way, at least, to
> make the consumer code uncompilable after such change?).

If a class you are using is modified so that it now implements the
IDisposable interface and it has the complete Dispose pattern implemented
correctly it will NOT break your code if you aren't calling Dispose on the
modified class - it just means the use of the modified class by you is sub
optiminal from a resource (GC) perspective, the GC will call the finalizer
added to the modified class which should call the 'Dispose' method. You jsut
won't know when this will be as the modified class is not being used in a
'using' block or having the 'Dispose' method called directly.

HTH

Ollie
Jon Skeet [C# MVP] - 21 Feb 2008 11:22 GMT
<snip>

> If a class you are using is modified so that it now implements the
> IDisposable interface and it has the complete Dispose pattern implemented
> correctly it will NOT break your code if you aren't calling Dispose on the
> modified class - it just means the use of the modified class by you is sub
> optiminal from a resource (GC) perspective, the GC will call the finalizer
> added to the modified class which should call the 'Dispose' method.

However, whether or not waiting for the finalizer thread to kick in
breaks your code depends on what it's hanging on to. If it's hanging on
to a database connection, for instance, it could easily break your code
- you might have as many of these objects as there are poolable
connections, for instance.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Ollie - 05 Mar 2008 14:23 GMT
> However, whether or not waiting for the finalizer thread to kick in
> breaks your code depends on what it's hanging on to. If it's hanging on
> to a database connection, for instance, it could easily break your code
> - you might have as many of these objects as there are poolable
> connections, for instance.

Surely the code in the example you quote would have this problem anyway?

Ollie
Jon Skeet [C# MVP] - 05 Mar 2008 14:37 GMT
> > However, whether or not waiting for the finalizer thread to kick in
> > breaks your code depends on what it's hanging on to. If it's hanging on
[quoted text clipped - 3 lines]
>
> Surely the code in the example you quote would have this problem anyway?

Which code? I didn't quote any example code. (I didn't see any code in
any post in this thread, in fact.)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Ollie Riches - 05 Mar 2008 17:38 GMT
> > > However, whether or not waiting for the finalizer thread to kick in
> > > breaks your code depends on what it's hanging on to. If it's hanging on
[quoted text clipped - 10 lines]
> Jon Skeet - <sk...@pobox.com>http://www.pobox.com/~skeet  Blog:http://www.msmvps.com/jon.skeet
> World class .NET training in the UK:http://iterativetraining.co.uk

Sorry I meant scenario 'if it's hanging on to a database connection'
because even without the class implementing the dispose pattern the
database (handle) with still be finalized

Ollie Riches
Jon Skeet [C# MVP] - 05 Mar 2008 18:04 GMT
> > > > However, whether or not waiting for the finalizer thread to kick in
> > > > breaks your code depends on what it's hanging on to. If it's hanging on
[quoted text clipped - 10 lines]
> because even without the class implementing the dispose pattern the
> database (handle) with still be finalized

Yes - but the point is that introducing this situation is effectively a
breaking change. With deterministic destructors, it wouldn't be. I'm
not saying deterministic destructors are feasible without other
problems, but I'm putting it in the context of the original post.

Implementing IDisposable when it wasn't implemented before is putting
an additional responsibility on the caller, without them being aware of
it.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
World class .NET training in the UK: http://iterativetraining.co.uk

Brian Gideon - 05 Mar 2008 18:25 GMT
> Now the problem: suppose you use some class, which does not implement
> IDisposable. All is well. And then, the maintainer of this class
[quoted text clipped - 5 lines]
> although the only thing that really has changed is just implementation
> of a class you use.

Adding IDisposable to a class is a version breaking change.  It may
not be a binary breaking change which would mean your code still
compiles, but it is a sematic breaking change since the behavior of
the class changes.  The documentation even mentions this...somewhere.

The only option the maintainer has is to create a brand new class that
implements IDisposable.  That way the original class remains untouched
and backward compatible with existing code.

By the way, adding an interface after the fact can be a binary
breaking change if it were added to an abstract base class.
Subclasses would have to define their own implementation or else
things won't compile.
Brian Gideon - 05 Mar 2008 18:31 GMT
> By the way, adding an interface after the fact can be a binary
> breaking change if it were added to an abstract base class.
> Subclasses would have to define their own implementation or else
> things won't compile.

Err...you could provide a default implementation.  What I meant to say
was adding a method to an interface can be a binary breaking change.
And of course it wouldn't matter if it were implemented by a concrete
or abstract class.  I was mixing different concepts in my mind
and...aww...nevermind...I obviously got that point completely wrong :)

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.