Can I have back my destructors, please?

C

cctv.star

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?).
 
A

Arne Vajhøj

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?).

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
 
S

Scott Roberts

Arne Vajhøj said:
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

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?
 
M

Marc Gravell

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
 
K

KWienhold

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

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
 
O

Ollie

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?).

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
 
J

Jon Skeet [C# MVP]

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.
 
P

Peter Duniho

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.

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
 
C

cctv.star

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.
 
J

Jeroen Mostert

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,

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.
 
C

cctv.star

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!
 
O

Ollie

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
 
J

Jon Skeet [C# MVP]

Ollie said:
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.)
 
O

Ollie Riches

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

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
 
J

Jon Skeet [C# MVP]

Ollie Riches said:
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

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.
 
B

Brian Gideon

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.

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.
 
B

Brian Gideon

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 :)
 
J

JS

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top