473,386 Members | 1,815 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

RFC: Exceptions and control flow

Exceptions must not be used to control program flow. I intend to show that
this statement is flawed.

In some instances, exceptions may be used to control program flow in ways
that can lead to improved code readability and performance.

Consider an application that must eliminate duplicates in a list.

using system.collections;

//SETUP CODE initialize a list and provide a duplicate
using System.Collections;
ArrayList arr = new ArrayList(10);
for(int i = 0; i < 10;i++)
arr.Add(i.ToString());

//dupes
arr.Add(1.ToString());
//Exception to control program flow starts here
Hashtable ht = new Hashtable();
foreach(string str in new ArrayList(arr))
{
try
{
ht.Add(str, str);
}
catch(ArgumentException)
{
arr.Remove(str);
}
}
The algorithm essentially uses the unique constrains of the hashtable to
enforce uniqueness. Logically, the program flow is controlled by the
presence of exceptions.
This approach works well when there are minimal to moderate duplicates in
the list. The logic is also clear and concise and very efficient compared
with other contemporary
techniques to remove duplicates in a list which typically involve
duplicating and iterating a copy of the list.

Conclusion: It is not true that exceptions must not be used to control
program flow.

comments/queries/takers welcome.

--
Regards,
Alvin Bruney [Microsoft MVP ASP.NET]

[Shameless Author plug]
The Microsoft Office Web Components Black Book with .NET
Now Available @ http://www.lulu.com/owc
----------------------------------------------------------

Nov 16 '05 #1
9 1743
Alvin... I have violated this rule when the code worked, was efficient
and easy to read. However, I believe the argument not to use exceptions
to control program flow is valid in certain inner loops that result in a
lot of unwinding. I suspect that you could also use hasttable.contains
to write readable code.

Regards,
Jeff
Conclusion: It is not true that exceptions must not be used to control

program flow.<

*** Sent via Developersdex http://www.developersdex.com ***
Don't just participate in USENET...get rewarded for it!
Nov 16 '05 #2
Well, everything I have seen from MS says that the performance hit in
raising an error is greater than the performance hit for testing for the
error condition so I thought that testing your example would show the same
thing. It did not always hold true:

In the code I list below, I looped Alvin's code 1000 times using the
try/catch and 1000 times testing for the dupe before adding to the
HashTable. In a couple dozen tests, the try/catch always took very close to
2 seconds to complete the loop while testing for the dupe took very close to
10 milliseconds... In that case, the performance penalty of the exception
was very big.

Then I moved the addition of the dupe outside of the 1000 evolution loop -
that way the error condition only occurs once out of the 1000 times rather
than every time. In this case, the try/catch took about 5 milliseconds and
testing for the dupe took the same 10 milliseconds. Using the try/catch was
faster.

Granted, these tests are not exactly scientific but the differences were so
significant and repeated relatively consistently over dozens of times in a
short time period, I believe they're pretty representative of the real
world, as the real world exists on my PC anyway.

My conclusion:

1. If the error condition is very unlikely or is going to happen only a
very tiny percentage (0.1% in my test) of the time, the try/catch can be
faster than testing for a condition every time, even when the likelihood of
that condition existing is very low.

2. In all other cases, where the error condition is even 1% likelihood or
greater, then the exception has a significant performance hit.

DalePres
MCAD, MCDBA, MCSE


//SETUP CODE initialize a list and provide a duplicate

ArrayList arr = new ArrayList(10);

for(int i = 0; i < 10;i++)

arr.Add(i.ToString());

DateTime start;

DateTime end;

start = DateTime.Now;

Console.WriteLine(start.ToString("HH:mm:ss.fff"));

for (int x = 0; x < 1000; x++)

{

//dupes

arr.Add(1.ToString());

//Exception to control program flow starts here

Hashtable ht = new Hashtable();

foreach(string str in new ArrayList(arr))

{

try

{

ht.Add(str, str);

}

catch(ArgumentException)

{

arr.Remove(str);

}

}

}

end = DateTime.Now;

Console.WriteLine(end.ToString("HH:mm:ss.fff"));

start = DateTime.Now;

Console.WriteLine(start.ToString("HH:mm:ss.fff"));

for (int x = 0; x < 1000; x++)

{

//dupes

arr.Add(1.ToString());

//Exception to control program flow starts here

Hashtable ht = new Hashtable();

foreach(string str in new ArrayList(arr))

{

if (ht[str] == null)

ht.Add(str, str);

else

arr.Remove(str);

}

}

end = DateTime.Now;

Console.WriteLine(end.ToString("HH:mm:ss.fff"));

Console.ReadLine();


"Alvin Bruney [MVP]" <vapor at steaming post office> wrote in message
news:Oo***************@TK2MSFTNGP09.phx.gbl...
Exceptions must not be used to control program flow. I intend to show that
this statement is flawed.

In some instances, exceptions may be used to control program flow in ways
that can lead to improved code readability and performance.

Nov 16 '05 #3
<"Alvin Bruney [MVP]" <vapor at steaming post office>> wrote:
Exceptions must not be used to control program flow. I intend to show that
this statement is flawed.

In some instances, exceptions may be used to control program flow in ways
that can lead to improved code readability and performance.

Consider an application that must eliminate duplicates in a list.

using system.collections;

//SETUP CODE initialize a list and provide a duplicate
using System.Collections;
ArrayList arr = new ArrayList(10);
for(int i = 0; i < 10;i++)
arr.Add(i.ToString());

//dupes
arr.Add(1.ToString());
//Exception to control program flow starts here
Hashtable ht = new Hashtable();
foreach(string str in new ArrayList(arr))
{
try
{
ht.Add(str, str);
}
catch(ArgumentException)
{
arr.Remove(str);
}
}
How is that better than:

foreach (string str in new ArrayList(arr))
{
// Have we seen this before?
if (ht.ContainsKey(str))
{
arr.Remove(str);
}
else
{
ht[str] = str;
}
}

<snip>
Conclusion: It is not true that exceptions must not be used to control
program flow.


I agree with your conclusion, but I don't think your example is a
particularly good one. The places where I go against this rule (and
always feel bad doing it, to be honest) is where I basically need to
jump a couple of levels up the stack. For instance, testing lots of
different things:

try
{
TestFoo(1, 2, 3);
TestFoo(2, 3, 4);
TestBar();
TestFoo(5, 6, 7);
return true;
}
catch
{
return false;
}

where each of the Test methods throws an exception if the test fails
feels a lot less fragile (and clearer) than:

if (!TestFoo(1, 2, 3))
{
return false;
}
if (!TestFoo(2, 3, 4))
{
return false;
}
if (!TestBar())
{
return false;
}
if (!TestFoo(5, 6, 7))
{
return false;
}
return true;

(Admittedly in this case it could be reasonably reduced to a compound
"if", but in real life code the test lines are often more complicated,
leading to a huge and unreadable test when combined.)

This is only appropriate at all when the test methods are *solely*
there for the above purpose though - and that should be very clearly
documented.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Nov 16 '05 #4
DalePres <don-t-spa-m-me@lea-ve-me-a-lone--.com> wrote:
Well, everything I have seen from MS says that the performance hit in
raising an error is greater than the performance hit for testing for the
error condition so I thought that testing your example would show the same
thing. It did not always hold true:

In the code I list below, I looped Alvin's code 1000 times using the
try/catch and 1000 times testing for the dupe before adding to the
HashTable. In a couple dozen tests, the try/catch always took very close to
2 seconds to complete the loop while testing for the dupe took very close to
10 milliseconds... In that case, the performance penalty of the exception
was very big.
Were you running under the debugger by any chance? When running under
the debugger, the first exception thrown takes a couple of seconds, and
the rest are reasonably quick. This one-time performance penalty
doesn't occur under release.

On my laptop I can loop the code 100,000 times and still end up
finishing in about 3 seconds with exceptions. This is admittedly nearly
ten times slower than without exceptions, but the difference is much
closer. And don't forget, very few places in code end up being
bottlenecks.
Granted, these tests are not exactly scientific but the differences were so
significant and repeated relatively consistently over dozens of times in a
short time period, I believe they're pretty representative of the real
world, as the real world exists on my PC anyway.


If I'm right and you were running under the debugger, they aren't
representative of real world applications, which don't run under the
debugger.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Nov 16 '05 #5
I think that the performance issues still make exception testing faster than
exception raising even when you're talking about going back several levels
in code.

The primary consideration I use if I do consider using exceptions to control
program flow is cost to the client. If the programming cost of developing a
robust exception test is greater than the cost of a often-times
insignificant overall user interface performance degradation, then use an
exception to control expected program flow.

Of course, I don't have to say this to all you MVP's but I will say it for
any new C# programmers reading the thread: This whole question is about
handling known or expected exceptions. You still want to use
try/catch/finally loops liberally in your code where there exists a chance
of an unexpected exception. Letting an unhandled exception get to your
users is always a bigger performance hit than all the other options.

IMHO
DalePres
MCAD, MCDBA, MCSE
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
<"Alvin Bruney [MVP]" <vapor at steaming post office>> wrote:
Exceptions must not be used to control program flow. I intend to show
that
this statement is flawed.

In some instances, exceptions may be used to control program flow in ways
that can lead to improved code readability and performance.

Consider an application that must eliminate duplicates in a list.

using system.collections;

//SETUP CODE initialize a list and provide a duplicate
using System.Collections;
ArrayList arr = new ArrayList(10);
for(int i = 0; i < 10;i++)
arr.Add(i.ToString());

//dupes
arr.Add(1.ToString());
//Exception to control program flow starts here
Hashtable ht = new Hashtable();
foreach(string str in new ArrayList(arr))
{
try
{
ht.Add(str, str);
}
catch(ArgumentException)
{
arr.Remove(str);
}
}


How is that better than:

foreach (string str in new ArrayList(arr))
{
// Have we seen this before?
if (ht.ContainsKey(str))
{
arr.Remove(str);
}
else
{
ht[str] = str;
}
}

<snip>
Conclusion: It is not true that exceptions must not be used to control
program flow.


I agree with your conclusion, but I don't think your example is a
particularly good one. The places where I go against this rule (and
always feel bad doing it, to be honest) is where I basically need to
jump a couple of levels up the stack. For instance, testing lots of
different things:

try
{
TestFoo(1, 2, 3);
TestFoo(2, 3, 4);
TestBar();
TestFoo(5, 6, 7);
return true;
}
catch
{
return false;
}

where each of the Test methods throws an exception if the test fails
feels a lot less fragile (and clearer) than:

if (!TestFoo(1, 2, 3))
{
return false;
}
if (!TestFoo(2, 3, 4))
{
return false;
}
if (!TestBar())
{
return false;
}
if (!TestFoo(5, 6, 7))
{
return false;
}
return true;

(Admittedly in this case it could be reasonably reduced to a compound
"if", but in real life code the test lines are often more complicated,
leading to a huge and unreadable test when combined.)

This is only appropriate at all when the test methods are *solely*
there for the above purpose though - and that should be very clearly
documented.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Nov 16 '05 #6
I did the tests below starting the app within the IDE but using Ctrl-F5 to
start without debugging. You are right, of course, that debugging causes a
significant hit on the first exception but I don't know if it applies as I
was running or not.

Just to be sure though, I did run the exe from outside of the environment
and got similar results to yours, 70 ms with the exception, 10ms testing for
the duplicate.

So maybe instead of a 1% crossover point in the performance equation, it is
a 10% crossover point.

DalePres
Nov 16 '05 #7
DalePres <don-t-spa-m-me@lea-ve-me-a-lone--.com> wrote:
I think that the performance issues still make exception testing faster than
exception raising even when you're talking about going back several levels
in code.
Using a test will usually be faster than raising an exception, but
what's important is how much faster in the real world - how much faster
per operation, and how frequently the operation occurs. Very little
code is actually performance critical, and readability should be the
most important test.
The primary consideration I use if I do consider using exceptions to control
program flow is cost to the client. If the programming cost of developing a
robust exception test is greater than the cost of a often-times
insignificant overall user interface performance degradation, then use an
exception to control expected program flow.

Of course, I don't have to say this to all you MVP's but I will say it for
any new C# programmers reading the thread: This whole question is about
handling known or expected exceptions. You still want to use
try/catch/finally loops liberally in your code where there exists a chance
of an unexpected exception. Letting an unhandled exception get to your
users is always a bigger performance hit than all the other options.


That doesn't mean using lots of try/catch/finally blocks though - just
having a top-level one is often good enough.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too
Nov 16 '05 #8
Initially, I thought your case is more expensive because you always do a
look up even when it is not needed. Hashtables are optimized for retrievals
but there is still the overhead of the actual lookup.
However, the actually timed results does show you have faster code.
grrrrrrrrrrrrrrrrrrrr!!!
Release mode results

yours
150.216
130.1872
150.216

mine
170.2448
160.2334
150.216

test code
ArrayList arr = new ArrayList(10);

for(int i = 0; i < 100000;i++)

arr.Add(i.ToString());

arr.Add(1.ToString());

arr.Add(2.ToString());

arr.Add(3.ToString());

DateTime start = DateTime.Now;

Hashtable ht = new Hashtable();

foreach(string str in new ArrayList(arr))

{

try

{

ht.Add(str, str);

}

catch(ArgumentException)

{

arr.Remove(str);

}

}

// Have we seen this before?

// if (ht.ContainsKey(str))

// {

// arr.Remove(str);

// }

// else

// {

// ht[str] = str;

// }

// }
DateTime stop = DateTime.Now;

TimeSpan diff = stop.Subtract(start);
Console.WriteLine(diff.TotalMilliseconds);
--
Regards,
Alvin Bruney [Microsoft MVP ASP.NET]

[Shameless Author plug]
The Microsoft Office Web Components Black Book with .NET
Now Available @ http://www.lulu.com/owc
----------------------------------------------------------
"Jon Skeet [C# MVP]" <sk***@pobox.com> wrote in message
news:MP************************@msnews.microsoft.c om...
<"Alvin Bruney [MVP]" <vapor at steaming post office>> wrote:
Exceptions must not be used to control program flow. I intend to show
that
this statement is flawed.

In some instances, exceptions may be used to control program flow in ways
that can lead to improved code readability and performance.

Consider an application that must eliminate duplicates in a list.

using system.collections;

//SETUP CODE initialize a list and provide a duplicate
using System.Collections;
ArrayList arr = new ArrayList(10);
for(int i = 0; i < 10;i++)
arr.Add(i.ToString());

//dupes
arr.Add(1.ToString());
//Exception to control program flow starts here
Hashtable ht = new Hashtable();
foreach(string str in new ArrayList(arr))
{
try
{
ht.Add(str, str);
}
catch(ArgumentException)
{
arr.Remove(str);
}
}


How is that better than:

foreach (string str in new ArrayList(arr))
{
// Have we seen this before?
if (ht.ContainsKey(str))
{
arr.Remove(str);
}
else
{
ht[str] = str;
}
}

<snip>
Conclusion: It is not true that exceptions must not be used to control
program flow.


I agree with your conclusion, but I don't think your example is a
particularly good one. The places where I go against this rule (and
always feel bad doing it, to be honest) is where I basically need to
jump a couple of levels up the stack. For instance, testing lots of
different things:

try
{
TestFoo(1, 2, 3);
TestFoo(2, 3, 4);
TestBar();
TestFoo(5, 6, 7);
return true;
}
catch
{
return false;
}

where each of the Test methods throws an exception if the test fails
feels a lot less fragile (and clearer) than:

if (!TestFoo(1, 2, 3))
{
return false;
}
if (!TestFoo(2, 3, 4))
{
return false;
}
if (!TestBar())
{
return false;
}
if (!TestFoo(5, 6, 7))
{
return false;
}
return true;

(Admittedly in this case it could be reasonably reduced to a compound
"if", but in real life code the test lines are often more complicated,
leading to a huge and unreadable test when combined.)

This is only appropriate at all when the test methods are *solely*
there for the above purpose though - and that should be very clearly
documented.

--
Jon Skeet - <sk***@pobox.com>
http://www.pobox.com/~skeet
If replying to the group, please do not mail me too

Nov 16 '05 #9
Conclusion: It is not true that exceptions must not be used to control
program flow.


Related to the current framework, your tests are true.

However in my opinion is not said with that, that Microsoft or other
developpers from a framework should take the same approach as Microsoft does
now or that Microsoft has to do that in future.

What means for me that this is the same as using a bug.

Although I have the idea that Microsoft developpers use that in my opinion
as well. By instance with a non existing relation in the databinding, if
that is a valid situation (at databinding to a non existing child). However,
they can change that direct when the Microsoft Framework changes, so we can
think in another way about that.

Just my thought,

Cor
Nov 16 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: Olivier Parisy | last post by:
Hi all, I am new to Python (I just finished Guido's tutorial). I was very surprised to learn there that the StopIteration is used to end for loops in a standard iterator setting. I come from...
59
by: kk_oop | last post by:
Hi. I wanted to use exceptions to handle error conditions in my code. I think doing that is useful, as it helps to separate "go" paths from error paths. However, a coding guideline has been...
15
by: Bernard | last post by:
Hi All, I am not sure if I should be asking this question on clc or clc++. Let me try on both. I hope that this is not too trivial for the brilliant minds over here. I know that OOP questions...
22
by: Drew | last post by:
How do I know which exceptions are thrown by certain methods? For example, reading a file might throw an IO Exception, etc. In Java, the compiler won't even let you compile unless you put your...
11
by: C# Learner | last post by:
What type of exception should I throw when my code detects that a connection has dropped (i.e. NetworkStream.Read() returns 0)? Should I just throw a SocketException or should I create my own...
5
by: Miyra | last post by:
Hi. I'm working with an app that uses exceptions for control flow. These are code blocks where exceptions are thrown/caught regularly. A couple hundred exceptions occur per hour and they're caught...
14
by: dcassar | last post by:
I have had a lively discussion with some coworkers and decided to get some general feedback on an issue that I could find very little guidance on. Why is it considered bad practice to define a...
35
by: Arnaud Delobelle | last post by:
Hi all, Imagine I have three functions a(x), b(x), c(x) that each return something or raise an exception. Imagine I want to define a function that returns a(x) if possible, otherwise b(x),...
0
RedSon
by: RedSon | last post by:
Chapter 3: What are the most common Exceptions and what do they mean? As we saw in the last chapter, there isn't only the standard Exception, but you also get special exceptions like...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.