IDisposable, "using" keyword and "Close()" methods

W

Willem van Rumpt

Hi all,
coming from an unmanaged programming background, I took my time to
sort out the IDisposable and finalizer patterns. Just when I thought
I had it all conceptually neatly arranged, the "Close()" methods reared
their ugly (at least it would seem...)heads.

I was happily delving away in the .NET framework, investigating the
stream classes with the msdn and Lutz Roeder's .NET reflector, when I
stumbled upon the following:

(using BinaryReader as an example, though not the only "bad guy")
1) The MSDN : "This implementation of Close calls the Dispose method
passing a true value."
2) the Close() method in the BinaryReader is marked virtual
3) the "using" keyword will invoke the implemented IDisposable.Dispose()
on the expression you want to dispose

it looks to me like the "using" keyword in this case could be
potentially very dangerous. Nothing prevents me from descending from
BinaryReader, override the Close() method, and do some essential extra
work (marking a flag, log something, whatever), and everyone using my
class with the "using" keyword would have (at best) unnoticed side effects.

I can see the need for IDisposable, I can see the need for virtual
methods that will call Dispose on your behalf, but I don't get the need
for a "using" keyword for calling Dispose() for you, when (at least in
some cases) you might be short-circuiting things because you should've
called "Close()" (or whatever they choose to call it). The documentation
in the MSDN doesn't mention anywhere (that is, I couldn't find it...)
that you should use Close() instead of
Dispose() from the implemented interface.

Is (or should I mark) the "using" keyword "potentially dangerous"?

Thanks in advance,

Willem van Rumpt
 
J

Jon Skeet [C# MVP]

Is (or should I mark) the "using" keyword "potentially dangerous"?

No - failing to use "using" is potentially dangerous.

If you want to write a class which doesn't implement Dispose
appropriately - i.e. if calling Dispose isn't enough to clean up the
unmanaged resources - that's fine. There's nothing to stop you from
writing badly behaved classes in .NET - but it *is* relatively easy to
write *well*-behaved classes instead.
 
S

Stoitcho Goutsev \(100\) [C# MVP]

Hi Willem,

Close methods is added to stream, reader, writer, etc classes just because
it makes more sense for those classes to be closed. Why is it virtual?
Simply because normally when somethig work with streams the idea is to
abstract where the data actually goes it could be memory, disk, db, etc. So
normaly the code deals with the base class. In .NET disposing is the way to
release unmanaged resources, so you should write your classes with that in
mind.
Some times Close behaves differently according to some conditions. For
example look at the Form class. When the form is shown modalles calling
Close dispose the form object. When the form is shown as a modal dialog,
though, Close hides the form and doesn't destroyed (disposed) it.
One is sure thing, though, Dispose always destroy. So bare this in mind when
you write your classes
 
W

Willem van Rumpt

Jon said:
No - failing to use "using" is potentially dangerous.

If you want to write a class which doesn't implement Dispose
appropriately - i.e. if calling Dispose isn't enough to clean up the
unmanaged resources - that's fine. There's nothing to stop you from
writing badly behaved classes in .NET - but it *is* relatively easy to
write *well*-behaved classes instead.
I'm not talking unmanaged resources. I'm assuming that the Dispose()
part would be properly implemented, i.e. releasing all unmanaged
resources, BUT since the Close() method is virtual, it seems the class
can still work and have a function and a state which the Close() method
sets or invokes, which would not be invoked or set if using the "using"
keyword, even more, it has been explicitly marked "virtual" so
descendants can do their own thing before/after calling dispose.

From what you say, I think that I didn't grasp the meaning of
IDisposable correctly. Should I regard a "disposable" object as
"destroyed", not to be referenced again?.

Willem van Rumpt
 
S

Sunny

Hi,

Just override Dispose(bool) and there put your code. Like this:

using System;
using System.IO;

namespace Test {

public class TestReader : BinaryReader {

public TestReader(Stream stream) : base(stream)
{
}

protected override void Dispose(bool disposing)
{
Console.WriteLine("Dispose called.");
base.Dispose(disposing);
}
}

public class Test {
[STAThread]
public static void Main()
{
TestReader testReader = new TestReader(new MemoryStream());
testReader.Close();
}
}
}

Sunny
 
J

Jon Skeet [C# MVP]

Willem van Rumpt said:
I'm not talking unmanaged resources. I'm assuming that the Dispose()
part would be properly implemented, i.e. releasing all unmanaged
resources, BUT since the Close() method is virtual, it seems the class
can still work and have a function and a state which the Close() method
sets or invokes, which would not be invoked or set if using the "using"
keyword, even more, it has been explicitly marked "virtual" so
descendants can do their own thing before/after calling dispose.

For some classes, there may be things you can do with an object after
calling Close, but before calling Dispose.

It's interesting to note that Stream is rather different from
BinaryWriter - Dispose calls Close rather than the other way round.
From what you say, I think that I didn't grasp the meaning of
IDisposable correctly. Should I regard a "disposable" object as
"destroyed", not to be referenced again?.

When an object is disposed, it should usually either be ready to reuse
"from scratch" or shouldn't be reused at all - usually the latter.
Calling Dispose should be all that is required when one is finished
with an object, and it's very rare (IME) that it's good to call it
*before* one is finished with the object.
 
K

Kyril Magnos

LMFAO!!!

--
HTH

Kyril Magnos

|> Nothing prevents me from descending from
| > BinaryReader, override the Close() method, and do some essential extra
| > work (marking a flag, log something, whatever), and everyone using my
| > class with the "using" keyword would have (at best) unnoticed side
effects.
| >
|
| The IDisposable pattern wants you to do all of your extra work in the
overriden method aka "protected override void Dispose(bool disposing)". If
you put your extra work in there then the "using" keyword will work fine as
the keyword is going to call "void Dispose()" witch is implemented to call
your overriden "Dispose(true)" - hence doing your extra work...
|
| > Is (or should I mark) the "using" keyword "potentially dangerous"?
| >
|
| Don't forget that "using" is syntactically equivalent to try{}
finally{Dispose} so it's a good idea to use it. Actually I think the whole
IDispose thing is LAAAAMMMMMEEE! As a C++ guy I really really want a .NET
keyword "delete" that is defined as: immediately calls the object's
finalizer {if one exists} and then issues a GC.SuppressFinalizer() for the
object...
|
| But nooo nobody listens to me... Nope! I thought going into Iraq was a
dumb idea - all I wanted to do was confiscate Saudi Arabian oil and kick the
natives out into their endless beach. If we'd done that gas would be .10
per gallon, we would have flipped UBL the big time finger by confiscating
holy land, the Wahhabiist movement would have lost its funding, and we'd
have a HUGE base right next to Saddam Hussein while leaving HIM in token
power so that he can deal with the Iraqi people. But nooooo.... I get no
delete keyword and now I'm stuck with an estimated $30,000 per taxpayer bill
for a dumb headed plan to make the middle east safe for Israel....
|
| Nope nobody ever listens to me and that's why the world's a Flippin
mess...
|
|
|
|
|
 
J

Jon Skeet [C# MVP]

Richard said:
Don't forget that "using" is syntactically equivalent to try{}
finally{Dispose} so it's a good idea to use it. Actually I think the
whole IDispose thing is LAAAAMMMMMEEE! As a C++ guy I really really
want a .NET keyword "delete" that is defined as: immediately calls
the object's finalizer {if one exists} and then issues a
GC.SuppressFinalizer() for the object...

That doesn't allow for situations where an object can be reused, of
course, which Dispose allows for in theory, even if it's rarely used.

You'd also still want another keyword in order to put the delete within
a try/finally block automatically, IMO.
 

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