[Mono-list] Exceptions and error codes.

Thong (Tum) Nguyen tum@veridicus.com
Fri, 21 Mar 2003 09:33:09 +1200


> -----Original Message-----
> From: mono-list-admin@lists.ximian.com [mailto:mono-list-
> admin@lists.ximian.com] On Behalf Of Piers Haken
> Sent: Friday, 21 March 2003 8:59 a.m.
> To: tum@veridicus.com; mono-list@lists.ximian.com
> Subject: RE: [Mono-list] Exceptions and error codes.
> 
> > -----Original Message-----
> > From: Thong Nguyen [mailto:tum@veridicus.com]
> > Sent: Thursday, March 20, 2003 12:14 PM
> > To: mono-list@lists.ximian.com
> > Subject: [Mono-list] Exceptions and error codes.
> >
> >
> > Hiya guys,
> >
> > I recently read some recommendations on the GTK# mailing list
> > regarding the use of exceptions.
> 
> You make some good arguments, but I think most of it is irrelevant
> since, in order to remain compatible with .NET, Mono must behave the
> same way with respect to returning (or not) exceptions. The MSDN docs
> describe exactly what exceptions/return codes File.Exists and
File.Open
> should return, Mono should do the same (unless the docs are in error).
A
> lot of work has already been done on this. You'll notice that many of
> the test cases test whether or not the correct exception is being
> returned.


Definitely.  I was more referring to our own class libraries (such as
GTK#).


> 
> Of course, for new class libraries many of your arguments are valid.
> Although I'd argue that the case for exceptions in .NET isn't as
> clear-cut as you propose.
> 
> For example, since you're not required to declare the exceptions that
a
> method throws, it's just as easy to miss handling an exception as it
is
> to miss handling a return code.

That's true.  The issue of checked exceptions opens up another can of
worms that I'm not willing to open here ;-).

But remember, with exceptions you still have the security of knowing the
following statements won't execute.

> The argument that it's easy to forget to
> handle a return code is purely subjective. I've been writing COM code
in
> C++ since '95 and I've found that it's quite simple to get into the
> habit of checking the result of every call and doing it in a way
that's
> readable. 

Yes.  I've written a lot of COM code myself and have found that you do
get used to using the SUCCESS macro ;-).  It's just a pain when you want
to handle errors for a block of code in the same way -- which happens
very often.  In COM there is no choice; you *must* check and handle
every HRESULT.

> In fact I often find that code that explicitly checks return
> codes for each call is easier to read than code that relies on catch
> blocks, since you know exactly which error handling code is associated
> with each call. Catch locks are fine if you want to apply the same
> exception handling code to a whole bunch of code, but as soon as you
> have to nest them they become unwieldy.
> 
> For example, compare:
> 
> try
> {
> 	DoSomething ();
> 	try
> 	{
> 		DoSomethingElse ();
> 		// some more code here...
> 	}
> 	catch (Exception e)
> 	{
> 		// handle exception in DoSomething ()
> 	}
> }
> Catch (Exception e)
> {
> 	// handle exception in DoSomethingElse ()
> }

Typically, if you design a good exception hierarchy the code is a lot
tidier:

try
{
  DoSomething();
  DoSomethingElse();
}
catch (DoSomethingException e )
{
}
catch (DoSomethingElseException e)
{
}

Or you if you want to handle both the same way:

try
{
  DoSomething();
  DoSomethingElse();
}
catch (DoException e)
{
  // catch(exception) is bad cause you can swallow things
  // like OutOfMemorryException
}

You can write code for the "best case" (e.g. no error) and handle the
errors in a pseudo declarative way (with exceptions).  I'm a sucker for
declarative languages.

> 
> With
> 
> HRESULT hr;
> if (FAILED (hr = DoSomething ()))
> {
> 	// handle error in DoSomething ()
> }
> else if (FAILED (hr = DoSomethingElse ()))
> {
> 	// handle error in DoSomethingElse ()
> }
> else
> {
> 	// some more code here...
> }
> 
> 
> 
> Or, if you don't need nested catch blocks:
> 
> 
> HRESULT hr;
> if (FAILED (hr = DoSomething ()) ||
>     FAILED (hr = DoSomethingElse ()))
> {
> 	// handle error
> }
> else
> {
> 	// some more code here...
> }
> 
> Or, if you prefer your error handling to be at the end:
> 
> HRESULT hr;
> if (SUCCEEDED (hr = DoSomething ()) &&
>     SUCCEEDED (hr = DoSomethingElse ()))
> {
> 	// some more code here...
> }
> else
> {
> 	// handle error
> }
> 

The tidied try/catch looks nicer IMO :-).

What if you had 10 statements and they all should be handled the same
way?

Use this:

HRESULT hr;

if (SUCCEEDED (hr = DoSomething1()) &&
    SUCCEEDED (hr = DoSomething2()) &&
    SUCCEEDED (hr = DoSomething3()) &&
    SUCCEEDED (hr = DoSomething4()) &&
    SUCCEEDED (hr = DoSomething5()) &&
    SUCCEEDED (hr = DoSomething6()) &&
    SUCCEEDED (hr = DoSomething7()) &&
    SUCCEEDED (hr = DoSomething8()) &&
    SUCCEEDED (hr = DoSomething9()) &&
    SUCCEEDED (hr = DoSomething10()))
{
	// some more code here...
}
else
{
	// handle error
}

Or this:?

try
{
  DoSomething1();
  DoSomething2();
  DoSomething3();
  DoSomething4();
  DoSomething5();
  DoSomething6();
  DoSomething7();
  DoSomething8();
  DoSomething9();
  DoSomething10();
}
catch (DoException e)
{
}

All the best,

^Tum

---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.461 / Virus Database: 260 - Release Date: 10/03/2003