Problems Handling Errors Correctly

J

Jonathan Wood

I'm trying to figure out the best way to handle errors in my code.

I understand try...catch...finally just fine. But I find that in practice
(in large part due to the lack of deterministic finalization), it's rarely
that simple.

Consider the following code. In order to ensure my writer and reader object
clean up in a timely manner no matter what, I've added two using blocks. But
do I need another using block for webResponse? For that matter, how about
webRequest?

This is what has always bothered me about .NET. Doesn't using add it's own
try...catch...finally blocks? In the end, it's really not clear where I need
them and where I don't, so I end up with tons of them. And that can't be
good for performance. Any suggestions?

try
{
// Get string to be posted
string post = PostString;

HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.ContentLength = PostString.Length;
using (StreamWriter writer = new
StreamWriter(webRequest.GetRequestStream()))
{
writer.Write(post);
writer.Close();
}
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
using (StreamReader reader = new
StreamReader(webResponse.GetResponseStream()))
{
_rawResult = reader.ReadToEnd();
reader.Close();
}
webResponse.Close();
}
catch (Exception e)
{
_message = e.Message;
}

Jonathan
 
P

Peter Duniho

Jonathan said:
I'm trying to figure out the best way to handle errors in my code.

I understand try...catch...finally just fine. But I find that in
practice (in large part due to the lack of deterministic finalization),
it's rarely that simple.

Actually, it is simple.
Consider the following code. In order to ensure my writer and reader
object clean up in a timely manner no matter what, I've added two using
blocks. But do I need another using block for webResponse?

You need webResponse to be in a using block, yes. But note that since
the "using() { }" block itself qualifies as a statement, you can simply
put two using statements with each other:

using (HttpWebResponse webResponse =
(HttpWebResponse)webRequest.GetResponse())
using (StreamReader reader = new
StreamReader(webResponse.GetResponseStream()))
{
// code here
}

If you prefer, you could do this instead, which is equivalent to the above:

using (HttpWebResponse webResponse =
(HttpWebResponse)webRequest.GetResponse(),
StreamReader reader = new
StreamReader(webResponse.GetResponseStream()))
{
// code here
}

It need not be messy.
For that
matter, how about webRequest?
No.

This is what has always bothered me about .NET. Doesn't using add it's
own try...catch...finally blocks? In the end, it's really not clear
where I need them and where I don't, so I end up with tons of them. And
that can't be good for performance. Any suggestions?

Yes. If the class implements IDisposable, then you need the "using"
statement. If it doesn't, you don't.

Pete
 
J

Jonathan Wood

Peter,
Actually, it is simple.

Well now, I do recall how hard the .NET developers tried to find a solution
to the lack of deterministic finalization, so I don't quite agree that "it
is simple" tells the entire story.
You need webResponse to be in a using block, yes. But note that since the
"using() { }" block itself qualifies as a statement, you can simply put
two using statements with each other:

using (HttpWebResponse webResponse =
(HttpWebResponse)webRequest.GetResponse())
using (StreamReader reader = new
StreamReader(webResponse.GetResponseStream()))
{
// code here
}

If you prefer, you could do this instead, which is equivalent to the
above:

using (HttpWebResponse webResponse =
(HttpWebResponse)webRequest.GetResponse(),
StreamReader reader = new
StreamReader(webResponse.GetResponseStream()))
{
// code here
}

I do prefer, but this gives me the error: Cannot use more than one type in a
for, using, fixed, or declaration statement.

The first version does compile.
It need not be messy.


No.

Because it doesn't implement IDisposable?
Yes. If the class implements IDisposable, then you need the "using"
statement. If it doesn't, you don't.

And what's the quickest way to find out? If it has a Dispose() method?

Also, can you tell me if the using statement eliminates the need to call the
Close() method on these objects?

Thanks.

Jonathan
 
P

Peter Duniho

Jonathan said:
Well now, I do recall how hard the .NET developers tried to find a
solution to the lack of deterministic finalization, so I don't quite
agree that "it is simple" tells the entire story.

I'm referring to the statement in your first that says that the question
of handling errors is "rarely that simple". Certainly there are things
about finalization that can complicate things, but these generally only
come up if you don't write the code correct. If you handle exceptions
as you should and ensure that objects are disposed when they should be,
it really is "that simple".
[...]
If you prefer, you could do this instead, which is equivalent to the
above:

using (HttpWebResponse webResponse =
(HttpWebResponse)webRequest.GetResponse(),
StreamReader reader = new
StreamReader(webResponse.GetResponseStream()))
{
// code here
}

I do prefer, but this gives me the error: Cannot use more than one type
in a for, using, fixed, or declaration statement.

Sorry...my mistake. Now that I think about it, I believe that the
variable declaration inside the "using" statement has to be the same
syntax as a legal declaration anywhere else, while at the same time
semi-colons are not allowed (ie, it has to be a single statement). This
means that you can only declare objects of a single type for any one
using statement.

So you'll have to go with the nested using statements instead.
Hopefully not a big deal.
[...]

Because it doesn't implement IDisposable?
Exactly.
Yes. If the class implements IDisposable, then you need the "using"
statement. If it doesn't, you don't.

And what's the quickest way to find out? If it has a Dispose() method?

Look at the documentation. Every .NET class has on the first doc page
for that class a complete description of all classes it inherits and
interfaces it implements. If the class implements IDisposable, it will
say so there.

Alternatively, you could always just look for the Dispose() or
IDisposable.Dispose() interface in the class methods.
Also, can you tell me if the using statement eliminates the need to call
the Close() method on these objects?

Generally speaking it should.

However, I can't rule out the possibility of a class that requires
explicit closing for best results. A Close() method may have other
semantics than simply disposing; I don't think that a class is required
to perform a Close() per se in its Dispose() method, so for example if
Close() flushes some buffers implicitly, you may not have a guarantee
that Dispose() would also do so (even though in many classes it very
well might).

Personally, I would go ahead and Close() as well. I treat Close() and
similar methods as distinct from Dispose(), even where they appear to
duplicate behavior.

Pete
 
C

Carl Daniel [VC++ MVP]

Jonathan said:
Peter,


Well now, I do recall how hard the .NET developers tried to find a
solution to the lack of deterministic finalization, so I don't quite
agree that "it is simple" tells the entire story.


I do prefer, but this gives me the error: Cannot use more than one
type in a for, using, fixed, or declaration statement.

The first version does compile.


Because it doesn't implement IDisposable?
Correct.


And what's the quickest way to find out? If it has a Dispose() method?

....or RTFM.
Also, can you tell me if the using statement eliminates the need to
call the Close() method on these objects?

Yes, it does.

-cd
 
J

Jonathan Wood

Peter,
Sorry...my mistake. Now that I think about it, I believe that the
variable declaration inside the "using" statement has to be the same
syntax as a legal declaration anywhere else, while at the same time
semi-colons are not allowed (ie, it has to be a single statement). This
means that you can only declare objects of a single type for any one using
statement.

Right, thanks. I'm still a little concerned about the overhead associated
with layers of exception handling. I don't know enough about .NET to know
what's going on under the covers but it sure seems like a lot is going on
once you get exception handling nested 3 or 4 levels deep.
Look at the documentation. Every .NET class has on the first doc page for
that class a complete description of all classes it inherits and
interfaces it implements. If the class implements IDisposable, it will
say so there.

I understand what you're saying but I'm not really seeing that. For example:
http://msdn2.microsoft.com/en-us/library/system.net.httpwebresponse.aspx. I
guess I have to then lookup the base class, WebResponse before I can finally
see it implements IDisposable. Yeah, that's kind of a PITA in my opinion.
Alternatively, you could always just look for the Dispose() or
IDisposable.Dispose() interface in the class methods.

Looking at HttpWebResponse, looks like that's not reliable.
Personally, I would go ahead and Close() as well. I treat Close() and
similar methods as distinct from Dispose(), even where they appear to
duplicate behavior.

Yeah, I suppose you're right. Although I can't imagine a well-written class
that stays "open" after calling Dispose().

Thanks.

Jonathan
 
J

Jonathan Wood

Carl,
...or RTFM.

I guess so. Seems kind of lame though. In the case of HttpWebResponse, it
means looking up base classes before there's any indication of IDisposable.
Given that virtually everything is a class in .NET, I hate to use that
approach for every class I use. I thought the whole point of OOP was that
you didn't need to understand the internal workings of the objects you used.
At the very least, I think the docs for each class should indicate if it
implements IDisposable. Anyway, don't get me started. <g>

Thanks.

Jonathan
 
P

Peter Duniho

Jonathan said:
Right, thanks. I'm still a little concerned about the overhead
associated with layers of exception handling. I don't know enough about
.NET to know what's going on under the covers but it sure seems like a
lot is going on once you get exception handling nested 3 or 4 levels deep.

Well, I think any concern about performance overhead is overblown. The
fact is, to unroll local allocations and the like during error handling,
you have to do very much the same sort of thing that setting up an
exception handling block does anyway.

More importantly, you don't really have any choice. Exceptions can be
thrown. You have to handle them. So, even if there is a performance
issue, that's just the price of using .NET.
I understand what you're saying but I'm not really seeing that. For
example:
http://msdn2.microsoft.com/en-us/library/system.net.httpwebresponse.aspx.
I guess I have to then lookup the base class, WebResponse before I can
finally see it implements IDisposable. Yeah, that's kind of a PITA in my
opinion.

Well, it's true that only the class that actually implements the
interface will document that. However, you need not trace down the
entire inheritance hierarchy to find that out A more reliable,
one-stop-shopping page is the class's "Members" doc page, which will
include interface members that are implemented in the class, whether
they are direct or inherited.
Looking at HttpWebResponse, looks like that's not reliable.

Yes...you have to be looking at the correct doc page, that's true.
Either WebResponse, or the HttpWebResponse "Members" page.

Knowing how to use the documentation is half the battle. To some
extent, that requires simply practicing reading the docs.
Yeah, I suppose you're right. Although I can't imagine a well-written
class that stays "open" after calling Dispose().

It's not really so much the question of a class staying open. It's
whether calling Close() does something different than Dispose().

For example, a class that handles buffered i/o is not required by .NET
to flush the buffers when Dispose() is called, but it may be that
Close() is documented to do so. Failing to call Close() before you call
Dispose() could result in lost data.

You'd have to look at each class specifically to see what they document;
presumably they'd include some guidance as to whether you can get away
with just calling Dispose(). But absent a specific statement one way or
the other, I would not assume that a close that implements both treats a
Dispose() as equivalent to Close().

It may in fact be true in most of the cases, but there's no way to
guarantee it. IMHO, you should only treat Dispose() as a substitute for
Close() if the class specifically says that it is.

Pete
 
J

Jonathan Wood

Peter,
Well, it's true that only the class that actually implements the interface
will document that. However, you need not trace down the entire
inheritance hierarchy to find that out A more reliable, one-stop-shopping
page is the class's "Members" doc page, which will include interface
members that are implemented in the class, whether they are direct or
inherited.

Well, okay, I guess you're talking about near the bottom of that page? I'm a
little confused because, according to Intellisense, HttpWebResponse has no
Dispose method. Yet, near the bottom of the members page, it does list
System.IDisposable.Dispose. I guess that's what you're talking about. I'll
just have to start looking there for each object I use and see if that
provides consistent info.
Yes...you have to be looking at the correct doc page, that's true. Either
WebResponse, or the HttpWebResponse "Members" page.

I was thinking more along the lines of Intellisense seeming to indicate that
HttpWebResponse has no Dispose method, and that's why I said looking for the
Dispose method may not be a reliable method of determining if a class
implements IDisposable. Am I off track here?
Knowing how to use the documentation is half the battle. To some extent,
that requires simply practicing reading the docs.

Well, I started developing software in 1987 and I've pretty much used MS
stuff so I'm thinking I have some idea how to use the help. But I guess you
mean figuring out where they hide stuff that lets me know if I have to worry
about a class cleaning up on time. Yeah, I'll spend some time on that.
(Although I don't have to like the way that is set up. said:
It's not really so much the question of a class staying open. It's
whether calling Close() does something different than Dispose().

Well, I put "open" in quotes. I just meant whatever is needed to clean up.

Thanks.

Jonathan
 
P

Peter Duniho

Jonathan said:
Well, okay, I guess you're talking about near the bottom of that page?
I'm a little confused because, according to Intellisense,
HttpWebResponse has no Dispose method. Yet, near the bottom of the
members page, it does list System.IDisposable.Dispose.

Yes. It's an explicit implementation, which means you can only access
the method via the interface IDisposable. So using Intellisense, you
need to qualify the method as HttpWebResponse.IDisposable.Dispose or
cast the instance to IDisposable.
[...]
I was thinking more along the lines of Intellisense seeming to indicate
that HttpWebResponse has no Dispose method, and that's why I said
looking for the Dispose method may not be a reliable method of
determining if a class implements IDisposable. Am I off track here?

While I find it very useful, IMHO Intellisense is not a substitute for
reading the documentation. :)

Nevertheless, Intellisense usually can lead you to the correct
information about what a class implements, if you know the magic
incantation to get it to show you that. As with the documentation, it's
a matter of gaining experience with it.
Well, I started developing software in 1987 and I've pretty much used MS
stuff so I'm thinking I have some idea how to use the help.

Um. Okay. That's why you knew where to find out whether a class
implements IDisposable or not? :)

Seriously though, your past experience is not necessarily going to help
you with things you are unfamiliar with. I've been programming a fair
bit longer than you have, but I'm still practically a newbie when it
comes to .NET and C#. Practically every day I run across something that
my past experience hasn't helped me at all with.

So, no matter how experienced you are, it is often helpful to keep in
mind that there are always new things to learn. It's human nature to
blame the docs or the framework or whatever, but usually if you can just
try to put yourself in the same mindset as the human being who designed
the docs or the framework or whatever, things that seem arbitrary and a
"pain in the ass" turn out to just be a part of an
unfamiliar-but-consistent design.

In the case of interface documentation, every class's "Members" page
includes at the bottom a section that describes the interfaces
implemented by the class (or at least the explicit ones...you have to
look under the regular methods sections also to see if IDisposable() was
implemented implicitly; and no I don't know if there's a general rule
for which classes implement it explicitly and which implement it
implicitly).

The classes themselves might not be completely consistent, but there is
a single doc page that will tell you everything you need to know, and
the documentation itself is consistent. It's very predictable how to
find out the information, once you become accustomed to looking for it.
MSDN definitely is not "hiding" this information from you.
[...]
It's not really so much the question of a class staying open. It's
whether calling Close() does something different than Dispose().

Well, I put "open" in quotes. I just meant whatever is needed to clean up.

Well, then you aren't necessarily talking about a class with a Close()
method then.

Pete
 
D

Doug Semler

Peter Duniho said:
Yes. It's an explicit implementation, which means you can only access the
method via the interface IDisposable. So using Intellisense, you need to
qualify the method as HttpWebResponse.IDisposable.Dispose or cast the
instance to IDisposable.

This is interesting that the IDisposable is implemented in this class
against all of the design patterns that I have seen "reccomended" for
IDisposable types, namely
Interface explicitly implemented in a non sealed class
IDisposable implemented in a manner that is quite different from other
IDisposable implementations in the framework...

Wonder why they did it that way...

Just a note, I also found that, at least with the framework classes, the
presence of a Close method strongly indicates that I should ensure that it
is not ultimately derived from IDisposable. While FxCop is good in most
cases of finding me IDisposable types I forget to dispose, it misses the
ones (like HttpWebResponse) that are not constructed directly from my code
but returned by other functions (guess it assumes that the other object
"owns" the return value given to me?). However, whenever I run across a
class that has a Close() method, I double check to see if it is IDisposable
(it normally is)....

--
Doug Semler, MCPD
a.a. #705, BAAWA. EAC Guardian of the Horn of the IPU (pbuhh).
The answer is 42; DNRC o-
Gur Hfrarg unf orpbzr fb shyy bs penc gurfr qnlf, abbar rira
erpbtavmrf fvzcyr guvatf yvxr ebg13 nalzber. Fnq, vfa'g vg?
 
P

Peter Duniho

Doug said:
This is interesting that the IDisposable is implemented in this class
against all of the design patterns that I have seen "reccomended" for
IDisposable types, namely
Interface explicitly implemented in a non sealed class
IDisposable implemented in a manner that is quite different from other
IDisposable implementations in the framework...

Wonder why they did it that way...

Me too. I would be one of the first to agree that the .NET class design
has some inconsistencies in it. :)
Just a note, I also found that, at least with the framework classes, the
presence of a Close method strongly indicates that I should ensure that
it is not ultimately derived from IDisposable.

While I can see the value in encouraging that a class implement one or
the other but not both, that's certainly contrary to a number of
well-used classes in .NET. Do you have some specific guidance in the
..NET documentation that suggests a class with Close() should not
implement IDisposable?
[...]
However, whenever I
run across a class that has a Close() method, I double check to see if
it is IDisposable (it normally is)....

If it normally is, isn't that contrary to the "strong indication" you
mention above?

Pete
 
J

Jonathan Wood

Peter,
Seriously though, your past experience is not necessarily going to help
you with things you are unfamiliar with. I've been programming a fair bit
longer than you have, but I'm still practically a newbie when it comes to
.NET and C#. Practically every day I run across something that my past
experience hasn't helped me at all with.

Ah, well you are an old timer then. <g>

Seriously, I have spent a lot of time looking at .NET documentation. In many
respects, I find it severely lacking where many methods or properties have a
tiny description that provides no more information than simply looking at
the name of the member. But, yes, there are ways to find certain things if
you figure out where they are. And I'll contunue to examine the online help,
but I'm still not happy with much of it and think much could've been done
better.
So, no matter how experienced you are, it is often helpful to keep in mind
that there are always new things to learn. It's human nature to blame the
docs or the framework or whatever, but usually if you can just try to put
yourself in the same mindset as the human being who designed the docs or
the framework or whatever, things that seem arbitrary and a "pain in the
ass" turn out to just be a part of an unfamiliar-but-consistent design.

As I mentioned, I had the opportunity to have conversations with several of
the .NET developers when it first came out. I have some idea where they are
coming from. Personally, I think some of the issues with the .NET
documentation is simply due to the depth of the product and the priority, or
lack thereof, of being more thorough.

BTW, one thing I do recall saying to the developers was that the docs should
clearly indicate to me whether or not a particular method (or property) can
possibly raise an exception. At the time, they agreed but I'm also having
trouble seeing any indication of that. Do you happen to know the "magic
incantation" to obtain that information?

Thanks.

Jonathan
 
D

Doug Semler

Peter Duniho said:
Me too. I would be one of the first to agree that the .NET class design
has some inconsistencies in it. :)


While I can see the value in encouraging that a class implement one or the
other but not both, that's certainly contrary to a number of well-used
classes in .NET. Do you have some specific guidance in the .NET
documentation that suggests a class with Close() should not implement
IDisposable?

I think you misunderstood what I meant. All I was implying was that the
classes in the framework that have Close() *tend* to also be IDisposable
for whatever reason. (Streams come to mind). Well. Streams are a bad
example, they all seem to have Close() functions that basically call
Dispose(). Maybe the DbConnection stuff. Not saying it's the other way
around, just the presense of a Close() function tells me to look for
IDisposable. No documentation, just observations. But like I said, except
for object creations like: DbDataReader = DbConnection.ExecuteReader();
FxCop is pretty good at catching my dispose misses...And I agree that it's a
bad idea to assume that Close() calls Dispose() even though quite a few
classes exhibit that behavior...

I have other vents with respect to Close() and Dispose() and the stream
classes. Who here hasn't been confused as to the actual point at which bw,
gs, and fs get disposed?

using (FileStream fs = new FileStream(...))
{
using (GZipStream gs = new GZipStream(fs, CompressMode.Compress))
{
using (BinaryWriter bw = new BinaryWriter(gs))
{
//...
bw.Flush();
bw.Close();
}
}
}

--
Doug Semler, MCPD
a.a. #705, BAAWA. EAC Guardian of the Horn of the IPU (pbuhh).
The answer is 42; DNRC o-
Gur Hfrarg unf orpbzr fb shyy bs penc gurfr qnlf, abbar rira
erpbtavmrf fvzcyr guvatf yvxr ebg13 nalzber. Fnq, vfa'g vg?
 
J

Jonathan Wood

Doug,
I have other vents with respect to Close() and Dispose() and the stream
classes. Who here hasn't been confused as to the actual point at which
bw, gs, and fs get disposed?

using (FileStream fs = new FileStream(...))
{
using (GZipStream gs = new GZipStream(fs, CompressMode.Compress))
{
using (BinaryWriter bw = new BinaryWriter(gs))
{
//...
bw.Flush();
bw.Close();
}
}
}

Doesn't it occur simply at the closing curly brace of the corresponding
using block?

What can I say, I still find the above to be unattractive constructs and am
somewhat frustrated that limitations in the language/platform pretty much
force us to do things like that.

Jonathan
 
D

Doug Semler

Jonathan Wood said:
Doug,


Doesn't it occur simply at the closing curly brace of the corresponding
using block?

What can I say, I still find the above to be unattractive constructs and
am somewhat frustrated that limitations in the language/platform pretty
much force us to do things like that.


Believe it or not, no. bw, gs, and fs are all *Disposed* (not just Closed)
due to the call of bw.Close(). And even if I didn't put the Close()
statement in there, they would all be Disposed when bw gets Disposed.

It has to do with the fact that calling Close() a Stream (i think all of the
Framework ones at least, and the default base class implementation) Disposes
the Stream. And, in most (all?) of the Framework streams' Dispose functions
will also Dispose the underlying stream if it has one. So basically,
calling Close() on bw will call Dispose() on bw. bw has an underlying
stream (gs), so gs's Dispose function gets called in bw's Dispose. Since gs
holds an underlying stream (fs), it's Dispose function gets called!

--
Doug Semler, MCPD
a.a. #705, BAAWA. EAC Guardian of the Horn of the IPU (pbuhh).
The answer is 42; DNRC o-
Gur Hfrarg unf orpbzr fb shyy bs penc gurfr qnlf, abbar rira
erpbtavmrf fvzcyr guvatf yvxr ebg13 nalzber. Fnq, vfa'g vg?
 
P

Peter Duniho

Jonathan said:
Seriously, I have spent a lot of time looking at .NET documentation. In
many respects, I find it severely lacking where many methods or
properties have a tiny description that provides no more information
than simply looking at the name of the member.

I agree that the .NET docs can be overly sparse in some cases. Many,
even. But as with your observation about the .NET Framework itself, to
some extent this is a consequence of the sheer size of the Framework.
One hopes that over time, the documentation will get filled in, but it's
not hard to see why some things may have been considered lower priority
and receive a basic "template" documentation rather than a fully useful
description.

However, that template does include a complete enumeration of
interfaces, which is really all I was talking about here. I haven't run
across a class for which the docs didn't tell me what interfaces it
implements.

For what it's worth, I find MSDN to be FAR superior to other
documentation I've had to use, including recently. If you think MSDN is
sparse, go check out Apple's developer reference for their APIs, or
worse try browsing through man pages trying to figure out Unix stuff.
MSDN does a really great job of providing convenient search and
cross-references, which goes a long way toward being able to easily
connect the dots when a specific doc page is a bit sparse.
[...]
As I mentioned, I had the opportunity to have conversations with several
of the .NET developers when it first came out. I have some idea where
they are coming from. Personally, I think some of the issues with the
.NET documentation is simply due to the depth of the product and the
priority, or lack thereof, of being more thorough.

No doubt it is a balance between getting everything documented and
having the resources, whether due to cost-cutting measures or simply due
to the difficulty in finding qualified staff, to thoroughly document
everything.
BTW, one thing I do recall saying to the developers was that the docs
should clearly indicate to me whether or not a particular method (or
property) can possibly raise an exception. At the time, they agreed but
I'm also having trouble seeing any indication of that. Do you happen to
know the "magic incantation" to obtain that information?

My experience has been that possible exceptions are well-documented
within the method (I can't think of any properties that throw exceptions
off the top of my head, but I would expect any that do to follow the
same pattern). Specifically, the docs include a section immediately
after the "Return Value" section enumerating possible exceptions.

Pete
 
J

Jonathan Wood

Doug,
Believe it or not, no. bw, gs, and fs are all *Disposed* (not just
Closed) due to the call of bw.Close(). And even if I didn't put the
Close() statement in there, they would all be Disposed when bw gets
Disposed.

So the using statements for fs and gs are unnecessary in your example?
It has to do with the fact that calling Close() a Stream (i think all of
the Framework ones at least, and the default base class implementation)
Disposes the Stream. And, in most (all?) of the Framework streams'
Dispose functions will also Dispose the underlying stream if it has one.
So basically, calling Close() on bw will call Dispose() on bw. bw has an
underlying stream (gs), so gs's Dispose function gets called in bw's
Dispose. Since gs holds an underlying stream (fs), it's Dispose function
gets called!

I'm still pretty new to .NET but doesn't that prevent you from reusing a
closed stream? Or is the point that you would never want to reuse a closed
stream unless you reinitialized it? Or, better still, what if you wanted to
create another BinaryWriter on gs? You're saying you cannot once one
BinaryWriter based on gs has closed. That can't be a good thing can it?

Jonathan
 
J

Jonathan Wood

Peter,
I agree that the .NET docs can be overly sparse in some cases. Many,
even. But as with your observation about the .NET Framework itself, to
some extent this is a consequence of the sheer size of the Framework. One
hopes that over time, the documentation will get filled in, but it's not
hard to see why some things may have been considered lower priority and
receive a basic "template" documentation rather than a fully useful
description.

Makes me wonder if the existing documentation is based on the <summary> tags
in the source code. That would explain why they seem to be terse and very
slow to get updated.
My experience has been that possible exceptions are well-documented within
the method (I can't think of any properties that throw exceptions off the
top of my head, but I would expect any that do to follow the same
pattern). Specifically, the docs include a section immediately after the
"Return Value" section enumerating possible exceptions.

If that is the case, and it's complete, that would be great. If it's not the
case, or it's incomplete, or it leaves out exceptions that could be thrown
my methods called from the method, that would really be a drag.

Jonathan
 
D

Doug Semler

Jonathan Wood said:
Peter,


Makes me wonder if the existing documentation is based on the <summary>
tags in the source code. That would explain why they seem to be terse and
very slow to get updated.

The class documentation is (mostly) generated from code comments - I don't
remember the web site off hand but Google "SandCastle help generator" and
you'll find a utility that turns the xml file generated from the code
comments into MSDN style documentation (it is a public version of
Microsoft's tool they use to generate alot of the class documentation)
If that is the case, and it's complete, that would be great. If it's not
the case, or it's incomplete, or it leaves out exceptions that could be
thrown my methods called from the method, that would really be a drag.

It's pretty complete (exception documentation, that is) in my experience.
Every once in a while you'll get an exception that's thrown from deep in the
stack that doesn't seem intuitve, but for me it's usually when I'm dealing
with remoting or xml stuff. I would think that requiring any framework the
size of .NET to document all possible Exceptions that could be thrown from
calls to subfunctions is next to impossible (and I think MSDN's
documentation had even stated in the past at least that the <exception> doc
tag should document excpetions thrown directly from the function itself and
not those from sub functions.

--
Doug Semler, MCPD
a.a. #705, BAAWA. EAC Guardian of the Horn of the IPU (pbuhh).
The answer is 42; DNRC o-
Gur Hfrarg unf orpbzr fb shyy bs penc gurfr qnlf, abbar rira
erpbtavmrf fvzcyr guvatf yvxr ebg13 nalzber. Fnq, vfa'g vg?
 

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