Any reason to release memory-only streams?

  • Thread starter Thread starter Jeff Webb
  • Start date Start date
J

Jeff Webb

I'm embarassed to admit that I don't *really know* the answer to this one:

Is there any reason to Close a memory-only stream, such as a StringReader,
when I'm done with it? Or is it OK to just let it go out of scope without an
explicit Close?

A lot of times, I'd just like to use these on the fly, such as:

ds.ReadXml(New StringReader(xxx))

Or will that have unintended consequences in the long-run?

Thanks,

Jeff
 
Thanks, Matt, that's an excellent suggestion and the article provided good
detail.

If I'm reading the blog correctly there's no reason to Close the stream
since I know it's a memory-only resource. If there was any question (say it
was passed in as a reference), I should certainly Close it when done.

I'm using VB right now, but when I get back to C#, I'll remember the using
trick. It doesn't look like there's a VB equivalent.

-- Jeff
 
Hello Jeff,

I believe the rule of thumb is that if *you* create it, you should close it.

Meaning that if its passed byref, you should leave it alone (because the
user may want to do more with it).

I could be wrong, and if I am, Im sure the people here wont hesitate to let
me know. ;)
 
Yeah, what I meant was close it when I was done. I was making the assumption
I was working within code I control, an assumption I didn't state. My only
real question was: Do I need to worry about closing memory-only streams and
the answer seems to be No.
 
Jeff said:
Thanks, Matt, that's an excellent suggestion and the article provided
good detail.

If I'm reading the blog correctly there's no reason to Close the
stream since I know it's a memory-only resource. If there was any
question (say it was passed in as a reference), I should certainly
Close it when done.
I'm using VB right now, but when I get back to C#, I'll remember the
using trick. It doesn't look like there's a VB equivalent.

Well, there is actually. All using does is wrap the code in a try/finally
block and in the finally block it tests to see if the object implements
IDisposable, and if so, it calls Dispose. You have to write that code by
hand in VB.NET but its only a handful of lines to type.

In general, if an object implements IDisposable it means that it has a
resource that should be released as soon as possible, and hence you should
use the using or Try/Finally block as mentioned. However, there are
exceptions and unfortunately the documentation does not tell you about
these.

One example is Pens. This class has static/Shared properties like Black,
which will return a black Pen. Pen has a dispose method and it is based on a
GDI object so you *should* release a Pen as soon as possible. However, don't
do that with Pens. The reason is that this class has lazy initialization, so
the first time you access Pens.Black it will create a Black Pen and cache
this in a static field. The next time that you access Pens.Black it will
return the static field. If you call Dispose on this Pen then the object
will be disposed, but not released (the reference is not null/Nothing), so
Pens does not know that the Pen has been disposed and will return a disposed
object from Pens.Black. At this point there is no way to recreate the Pen
because Pens.Black checks to see if the reference is null, not if the object
is disposed.

The moral is that if an object has Dispose then you should use it... but
don't do this for all objects <g>.

Richard
 
<"Richard Grimes [MVP]" <read my sig>> wrote:

Msnip>
One example is Pens. This class has static/Shared properties like Black,
which will return a black Pen. Pen has a dispose method and it is based on a
GDI object so you *should* release a Pen as soon as possible. However, don't
do that with Pens. The reason is that this class has lazy initialization, so
the first time you access Pens.Black it will create a Black Pen and cache
this in a static field. The next time that you access Pens.Black it will
return the static field. If you call Dispose on this Pen then the object
will be disposed, but not released (the reference is not null/Nothing), so
Pens does not know that the Pen has been disposed and will return a disposed
object from Pens.Black. At this point there is no way to recreate the Pen
because Pens.Black checks to see if the reference is null, not if the object
is disposed.

Actually, having done a quick test, calling Dispose on Pens.Black
throws an ArgumentException. It still means you shouldn't call Dispose
on it, but at least the bug ends up being slightly more obvious :)
 
One example is Pens. This class has static/Shared properties like Black,
which will return a black Pen. Pen has a dispose method and it is based on
a GDI object so you *should* release a Pen as soon as possible. However,
don't do that with Pens. The reason is that this class has lazy
initialization, so the first time you access Pens.Black it will create a
Black Pen and cache this in a static field. The next time that you access
Pens.Black it will return the static field. If you call Dispose on this
Pen then the object will be disposed, but not released (the reference is
not null/Nothing), so Pens does not know that the Pen has been disposed
and will return a disposed object from Pens.Black. At this point there is
no way to recreate the Pen because Pens.Black checks to see if the
reference is null, not if the object is disposed.

The moral is that if an object has Dispose then you should use it... but
don't do this for all objects <g>.
This is terrible! IMO this is a design flaw or a bug. There is no way that
you should be able to Dispose an object that is not meant to ever be
disposed, or the Pens class should take action to prevent incorrect use, or
it should reset that field to null when Disposed so it can recreate it
properly when next used.
 
Jon said:
Actually, having done a quick test, calling Dispose on Pens.Black
throws an ArgumentException. It still means you shouldn't call Dispose
on it, but at least the bug ends up being slightly more obvious :)

Maybe its been fixed in 1.1, that's good. I found it first in a 1.0 app when
I got a disposed object exception some time after the fact, and I can tell
you it took me a while to find out where the dispose was called!

Richard
 
David said:
This is terrible! IMO this is a design flaw or a bug. There is no way
that you should be able to Dispose an object that is not meant to
ever be disposed, or the Pens class should take action to prevent
incorrect use, or it should reset that field to null when Disposed so
it can recreate it properly when next used.

As Jon's pointed out, its not quite as bad as I mentioned, the problem
exposes itself in a better way in 1.1 than I found in 1.0. However, you are
right. The Pen that is returned should not implement IDisposeable,
especially when there's language features like using.

<sigh> I keep coming across things like this in Windows Forms where the
developer really didn't think too carefully about how the feature is used,
or maybe ported the code from another system. I really would like to see the
C++ code for VB (classic) forms.

Richard
 
As Jon's pointed out, its not quite as bad as I mentioned, the problem
exposes itself in a better way in 1.1 than I found in 1.0. However, you
are right. The Pen that is returned should not implement IDisposeable,
especially when there's language features like using.
I don't feel that they've solved anything, just created a situation where
the problem will manifest itself somewhere else. Now the calling code has to
"know" that it needs to either exclude the black pen from being disposed, or
be prepared to deal with an exception, and then somehow "know" what the
correct course of action should be to deal with the exception. This is very
confusing, especially when apparently there are some pens for which it is
the correct course of action.
<sigh> I keep coming across things like this in Windows Forms where the
developer really didn't think too carefully about how the feature is used,
or maybe ported the code from another system. I really would like to see
the C++ code for VB (classic) forms.
That would clarify the current situation. I'd like to see them rationalize
this so that the behavior is consistent for all objects of that type. That
may mean two types of objects, ones that implement Dispose and ones that
don't, or deal internally with those that should not actually do anything
inside the Dispose method. In either case the calling code should really not
need to be that intimate with the implementation details.
|
In other words, I completely agree with your <sigh> :-)

Cheers,
Dave
 
Back
Top