It should be noted that structured exception handling allows you to nest try/catch/finally blocks within one another. This allows multiple levels of nesting and if the inner catch blocks cannot handle a specific exception type, the runtime will look for a matching catch handler in one of the outer try blocks. This repeats until a matching catch handler is found in one of the enclosing blocks. If the outermost try block is reached and no such matching catch handler is found, the runtime forces an unhandled exception to be raised. Let’s take
a look at a quick example of how to nest try/catch/finally blocks within one another.
Code listing in C#
using System;
class HelloNested
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("It's just too warm in here !");
}
catch(Exception exInner)
{
// Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message);
}
finally
{
// The inner finally block that executes always
Console.WriteLine("Inner finally");
}
// Continue execution in the Outer try block
Console.WriteLine("Continue executing in Outer ...");
}
catch(Exception exOuter)
{
// Display the exception message
Console.WriteLine("Outer catch caught an exception: {0}", exOuter.Message);
}
finally
{
// The outer finally block that executes always
Console.WriteLine("Outer finally");
}
}
}
Code listing in VB.NET
Imports System
Module HelloNested
Sub Main()
' This is the beginning of the Outer Try block
Try
' This is the beginning of the Inner Try block
Try
Throw New Exception("It's just too warm in here !")
Catch exInner As Exception
' Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", _ exInner.Message)
Finally
' The inner finally clause that executes always Console.WriteLine("Inner finally")
' The Inner Try/Catch/Finally blocks ends here
End Try
' Continue execution in the Outer try block
Console.WriteLine("Continue executing in Outer ...")
Catch exOuter As Exception
' Display the exception message
Console.WriteLine("Outer catch caught an exception: {0}", _ exOuter.Message)
Finally
' The outer finally clause that executes always Console.WriteLine("Outer finally")
' The Outer Try/Catch/Finally blocks ends here
End Try
End Sub
End Module
As shown in the example above, we have an inner try/catch/finally triad nested within an outer try block. The code within the inner try block raises an exception, so the runtime will check to see if one of the inner catch handlers will be able to handle the exception. Only when none of the inner catch handlers can handle that exception type, will the catch handlers of the outer try block be examined if they’ll be able to handle the error.
Save the example shown above in a file named HelloNested.cs/HelloNested.vb. Compile the application by running the following command from the DOS command line:
Compiling in C#
csc /target:exe HelloNested.cs
Compiling in VB.NET
vbc /target:exe HelloNested.vb
Run the program HelloNested.exe and observe the output:
Inner catch caught an exception : It's just too warm in here !
Inner finally
Continue executing in Outer ...
Outer finally
As seen from the output above, the inner catch handler catches an exception raised by the code in the inner try block since it can handle System.Exception exceptions. Now replace the inner catch block in the above example to handle only System.OverflowException exceptions.
Code listing in C#
catch(OverflowException exInner)
{
// Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message);
}
Code listing in VB.NET
Catch exOverflow As OverflowException
' Display the exception message
Console.WriteLine("Inner catch caught an overflow exception: {0}", _ exOverflow.Message)
Compile and run the modified program HelloNested.exe and observe the output:
Inner finally
Outer catch caught an exception : It's just too warm in here !
Outer finally
Notice that since the inner catch block can handle only System.OverflowException exceptions the runtime looks for a matching catch handler in the outer blocks and locates the outer catch handler, which subsequently handles the System.Exception exception.
Until now, we’ve seen how exceptions are raised by code enclosed within the try block. But also take note that you can throw exceptions from within catch and finally blocks too. There are times when a catch handler catches an exception and examines it only to find that it cannot handle the exception. In such cases, the catch handler can re-throw the exception hoping that one of the outer catch handlers will be able to catch the exception and handle it appropriately. In this case, the runtime checks for a matching catch handler
in one of the enclosing outer catch blocks to handle the re-thrown exception. Let’s modify the earlier example to re-throw the exception that we caught in the inner catch block. Modify the inner catch block in the HelloNested code as shown below:
Code listing in C#
// Rest of the code omitted for brevity . . .
try
{
try
{
throw new Exception("It's just too warm in here !");
}
catch(Exception exInner)
{
// Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message);
// Rethrow the exception
throw exInner;
}
finally
{
// The inner finally block that executes always
Console.WriteLine("Inner finally");
}
// Continue execution in the Outer try block
Console.WriteLine("Continue executing in Outer ...");
}
// Rest of the code omitted for brevity . . .
Code listing in VB.NET
' Rest of the code omitted for brevity . . .
' This is the beginning of the Inner Try block
Try
Throw New Exception("It's just too warm in here !")
Catch exInner As Exception
' Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message)
' Rethrow the exception
Throw exOverflow
Finally
' The inner finally clause that executes always
Console.WriteLine("Inner finally")
' The Inner Try/Catch/Finally blocks ends here
End Try
' Rest of the code omitted for brevity . . .
You will notice that the inner catch block re-throws the exception that it catches and hopes that one of the outer catch handlers will be able to handle it. Compile and run the application. Observe the output:
Inner catch caught an exception : It's just too warm in here !
Inner finally
Outer catch caught an exception : It's just too warm in here !
Outer finally
You’ll notice that both the inner and the outer catch handlers have a go at handling the exception. The inner catch block catches the exception and re-throws it. The re-thrown exception is then subsequently caught and handled by the outer catch handler. Take note that you can throw exceptions from finally blocks too.
How the CLR uses the call-stack to locate a matching catch handler:
When an exception occurs, the CLR tries to locate an appropriate catch handler (associated with the current try block), which is capable of handling the exception. If it cannot find an appropriate catch handler, then the next outer try-catch block is examined for appropriate catch handlers. This search continues until it finds a matching catch handler within the scope of the currently executing method (in C#) / procedure (in
VB.NET). If it still cannot find a matching catch handler within the scope of the currently executing method/procedure, it pops the current method/procedure out of the call-stack thus causing the current method to lose scope, and then searches for matching catch handlers in the next method (the method that had originally called the current method/procedure) in the call-stack. If it cannot find a matching catch handler there too, it pops this method/procedure out and examines the next one in the call-stack. This stack unwinding continues until a matching catch handler is found for the exception that was thrown. If no such matching catch handler is found when the stack is completely unwound, then the exception becomes an unhandled exception.
Code listing in C#
using System;
// Rest of the code omitted for brevity...
class Diver
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Get Set Go...");
// Call the DiveIn() static method
Diver.DiveIn();
}
catch(SharkAttackException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In Main finally");
}
}
static void DiveIn()
{
try
{
// Call the DiveDeeper static method
Diver.DiveDeeper();
}
catch(WaterTooColdException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In DiveIn finally");
}
}
static void DiveDeeper()
{
try
{
throw new SharkAttackException("Two hungry Great-White sharks " + "on the prowl");
}
catch(OutOfOxygenException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In DiveDeeper finally");
}
}
}
Code listing in VB.NETImports System
' Rest of the code omitted for brevity...
Module Diver
Sub Main()
Try
Console.WriteLine("Get Set Go...")
' Call the DiveIn() subroutine
Call DiveIn
Catch ex As SharkAttackException
Console.WriteLine(ex.Message)
Finally
' This should always get called
Console.WriteLine("In Main finally")
End Try
End Sub
Sub DiveIn()
Try
' Call the DiveDeeper() subroutine
Call DiveDeeper
Catch ex As WaterTooColdException
Console.WriteLine(ex.Message)
Finally
' This should always get called
Console.WriteLine("In DiveIn finally")
End Try
End Sub
Sub DiveDeeper()
Try
Throw New SharkAttackException("Two hungry Great-White sharks " + _
"on the prowl")
Catch ex As OutOfOxygenException
Console.WriteLine(ex.Message)
Finally
' This should always get called
Console.WriteLine("In DiveDeeper finally")
End Try
End Sub
End Module
Compile the application by running the following command from the DOS command line:
Compiling in C#
csc /target:exe Diver.cs
Compiling in VB.NET
vbc /target:exe Diver.vb
Run the program Diver.exe and observe the output:
Get Set Go...
In DiveDeeper finally
In DiveIn finally
Two hungry Great-White sharks on the prowl
In Main finally
In the above code fragment, the Main() entry point in the program calls the DiveIn() method (in C#) / Subroutine (in VB.NET), which in turn calls the DiveDeeper() method/subroutine. Notice that the DiveDeeper() method throws a custom application-defined exception called SharkAttackException.
Explanation using C#
Image
Explanation using VB.NET
Image
When the SharkAttackException exception is thrown the CLR checks to see if the catch handlers associated with the try block in DiveDeeper() can handle the exception Since the only exception type handled by the catch handler in DiveDeeper() happens to be OutOfOxygenException, the CLR will pop the DiveDeeper() method out of the stack after executing the finally block in DiveDeeper(). It will then go on to search for a suitable catch handler in the next method/procedure in the call-stack, which happens to be the DiveIn() method/subroutine. Since the try-catch block in the DiveIn() method also happens to have a catch handler that handles only WaterTooColdException exceptions, this method/procedure is also popped out of the call-stack after executing its finally block. The Main() method/subroutine, which is the next in the call-stack is then examined for matching catch handler within the try-catch block. As you can see, the try-catch block within the Main() method/subroutine does have a catch handler that can handle the SharkAttackException and so control is eventually passed over to this catch handler after which the corresponding finally block is executed. Assuming that the Main() entry-point method/subroutine did not have an appropriate catch handler too, then the next pop operation would have completely unwound the call-stack thereby making the SharkAttackException an unhandled exception
Catching arithmetic overflow exceptions with C#’s checked keyword:
Never ever discount the destructive effects that arithmetic overflow exceptions bring to
the stability of your software. For starters, recollect the tragic crash of the $7 billion
Ariane 5 rocket – A crash that resulted because its software system attempted to convert
a 64-bit floating-point number to a signed 16-bit integer, which subsequently caused an
overflow exception, and worse yet, there was no exception handler to handle this
exception. Sadly enough, the backup systems were also running on the same copy of the
software, without the exception handler. Read an account of the Ariane 5 crash at:
http://www.cs.ucsd.edu/users/goguen/courses/230/ariane5nyt.pdf
Let’s face it – How many times have we been in situations where we’ve stared in disbelief at our program spewing some insanely odd numerical output on arithmetic operations when it’s fairly obvious that the output is in not even slightly connected to what was expected of the program. Let’s quickly see what we mean here with an example. Consider the following C# program:
using System;
class ByteBites
{
static void Main(string[] args)
{
byte b1 = 57;
byte b2 = 200;
byte bResult = (byte)(b1 + b2);
Console.WriteLine("{0} + {1} = {2}", b1, b2, bResult);
}
}
Save this program to a file called ByteBites.cs. Compile the program using the following command from the DOS command line:csc /target:exe ByteBites.cs
Run ByteBites.exe and observe the output:
57 + 200 = 1
Many of us know what went wrong here. It’s fairly obvious here that a byte data type can hold only values from 0 to 255. Yet we are trying to add two bytes whose result over shoots the range of values that the resultant byte can hold, resulting in an overflow, and hence the absurd result 1. This is what the less wary among us (at least I do) run into when choosing data types to work with, paying little attention to the range of data that the program expects these data types to store and handle. A subtle arithmetic addition operation that has the potential to generate an overflow operation is enough to send your application to the tomb. Most often, good testing practices catch these bugs during the testing phase. But it certainly might get past the QA team if the test data being fed to the program is not very exhaustive and if every possible test case is not being taken into account – The Ariane 5 crash of 1996 is a testimony to that. So as developers, we need to
code defensively to catch such arithmetic overflow errors and handle them appropriately in the execution flow of the program as our application logic dictates, thus leaving no room for inconsistent or incorrect results to bring down the application to a grinding halt.
So let’s see where C# can help us here. C# provides the checked keyword to trap and handle such arithmetic overflows. When an arithmetic operation that is enclosed within a checked block (or checked expression) results in an overflow, the runtime generates a System.OverflowException. Compare this to our previous example where the overflow result was silently assigned to the resulting byte. So let’s modify the previous example to enclose the arithmetic addition of the two bytes within a checked block. The modified
code is shown below:
using System;
class ByteBites
{
static void Main(string[] args)
{
try
{
checked
{
byte b1 = 57;
byte b2 = 200;
byte bResult = (byte)(b1 + b2);
Console.WriteLine("{0} + {1} = {2}", b1, b2, bResult);
}
}
catch(OverflowException exOverflow)
{
Console.WriteLine("Caught overflow exception: {0}",
exOverflow.Message);
}
}
}
You’ll notice that we now have a checked block that encloses the arithmetic operation, which in turn is enclosed within a try block. Compile and run the application. Notice the output:
Caught overflow exception: Arithmetic operation resulted in an overflow.
You’ll notice that the addition operation generated a System.OverflowException because it was enclosed within a checked block. Remove the checked block and you’ll notice that you’ll again get back the cryptic 1 as the result. But then, wouldn’t it be asking too much if we had to put each and every arithmetic operation that had the potential to generate an overflow within a checked block. Thankfully, there’s an easier way to turn on arithmetic overflow checking for the entire application by using the /checked compiler option when
compiling your application. To test this, go ahead and remove the checked block that is enclosing the addition operation. This time compile the program with the /checked switch turned on, by typing the following command in the DOS command line:
csc /target:exe /checked ByteBites.cs
Run the program and observe the output that the program spews out:
Caught overflow exception: Arithmetic operation resulted in an overflow.
Notice that the /checked option has the same effect as using the checked block around your arithmetic operations. This option thereby allows you to enforce arithmetic overflow checks and to catch such exceptions throughout your application. So what if you’ve turned on the /checked option and want to selectively prevent certain parts in your application from generating an overflow exception when an overflow occurs. For example, assume you have a scenario where you need the check the value of the overflowed result to determine what action to take and so on. In such cases, you can use the unchecked keyword and enclose those arithmetic operations within an unchecked block so that an OverflowException is not generated for those operations. This is shown in the snippet of code below:
unchecked
{
byte b1 = 57;
byte b2 = 200;
byte bResult = (byte)(b1 + b2);
Console.WriteLine("{0} + {1} = {2}", b1, b2, bResult);
}
Of course, the above code fragment gives you yet another opportunity to see the infamous 1 as the result.
Using the checked keyword and /checked compiler option judiciously in your C# applications can help you catch arithmetic overflow exceptions and to ensure that your application stays sane.
a look at a quick example of how to nest try/catch/finally blocks within one another.
Code listing in C#
using System;
class HelloNested
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("It's just too warm in here !");
}
catch(Exception exInner)
{
// Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message);
}
finally
{
// The inner finally block that executes always
Console.WriteLine("Inner finally");
}
// Continue execution in the Outer try block
Console.WriteLine("Continue executing in Outer ...");
}
catch(Exception exOuter)
{
// Display the exception message
Console.WriteLine("Outer catch caught an exception: {0}", exOuter.Message);
}
finally
{
// The outer finally block that executes always
Console.WriteLine("Outer finally");
}
}
}
Code listing in VB.NET
Imports System
Module HelloNested
Sub Main()
' This is the beginning of the Outer Try block
Try
' This is the beginning of the Inner Try block
Try
Throw New Exception("It's just too warm in here !")
Catch exInner As Exception
' Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", _ exInner.Message)
Finally
' The inner finally clause that executes always Console.WriteLine("Inner finally")
' The Inner Try/Catch/Finally blocks ends here
End Try
' Continue execution in the Outer try block
Console.WriteLine("Continue executing in Outer ...")
Catch exOuter As Exception
' Display the exception message
Console.WriteLine("Outer catch caught an exception: {0}", _ exOuter.Message)
Finally
' The outer finally clause that executes always Console.WriteLine("Outer finally")
' The Outer Try/Catch/Finally blocks ends here
End Try
End Sub
End Module
As shown in the example above, we have an inner try/catch/finally triad nested within an outer try block. The code within the inner try block raises an exception, so the runtime will check to see if one of the inner catch handlers will be able to handle the exception. Only when none of the inner catch handlers can handle that exception type, will the catch handlers of the outer try block be examined if they’ll be able to handle the error.
Save the example shown above in a file named HelloNested.cs/HelloNested.vb. Compile the application by running the following command from the DOS command line:
Compiling in C#
csc /target:exe HelloNested.cs
Compiling in VB.NET
vbc /target:exe HelloNested.vb
Run the program HelloNested.exe and observe the output:
Inner catch caught an exception : It's just too warm in here !
Inner finally
Continue executing in Outer ...
Outer finally
As seen from the output above, the inner catch handler catches an exception raised by the code in the inner try block since it can handle System.Exception exceptions. Now replace the inner catch block in the above example to handle only System.OverflowException exceptions.
Code listing in C#
catch(OverflowException exInner)
{
// Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message);
}
Code listing in VB.NET
Catch exOverflow As OverflowException
' Display the exception message
Console.WriteLine("Inner catch caught an overflow exception: {0}", _ exOverflow.Message)
Compile and run the modified program HelloNested.exe and observe the output:
Inner finally
Outer catch caught an exception : It's just too warm in here !
Outer finally
Notice that since the inner catch block can handle only System.OverflowException exceptions the runtime looks for a matching catch handler in the outer blocks and locates the outer catch handler, which subsequently handles the System.Exception exception.
Until now, we’ve seen how exceptions are raised by code enclosed within the try block. But also take note that you can throw exceptions from within catch and finally blocks too. There are times when a catch handler catches an exception and examines it only to find that it cannot handle the exception. In such cases, the catch handler can re-throw the exception hoping that one of the outer catch handlers will be able to catch the exception and handle it appropriately. In this case, the runtime checks for a matching catch handler
in one of the enclosing outer catch blocks to handle the re-thrown exception. Let’s modify the earlier example to re-throw the exception that we caught in the inner catch block. Modify the inner catch block in the HelloNested code as shown below:
Code listing in C#
// Rest of the code omitted for brevity . . .
try
{
try
{
throw new Exception("It's just too warm in here !");
}
catch(Exception exInner)
{
// Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message);
// Rethrow the exception
throw exInner;
}
finally
{
// The inner finally block that executes always
Console.WriteLine("Inner finally");
}
// Continue execution in the Outer try block
Console.WriteLine("Continue executing in Outer ...");
}
// Rest of the code omitted for brevity . . .
Code listing in VB.NET
' Rest of the code omitted for brevity . . .
' This is the beginning of the Inner Try block
Try
Throw New Exception("It's just too warm in here !")
Catch exInner As Exception
' Display the exception message
Console.WriteLine("Inner catch caught an exception: {0}", exInner.Message)
' Rethrow the exception
Throw exOverflow
Finally
' The inner finally clause that executes always
Console.WriteLine("Inner finally")
' The Inner Try/Catch/Finally blocks ends here
End Try
' Rest of the code omitted for brevity . . .
You will notice that the inner catch block re-throws the exception that it catches and hopes that one of the outer catch handlers will be able to handle it. Compile and run the application. Observe the output:
Inner catch caught an exception : It's just too warm in here !
Inner finally
Outer catch caught an exception : It's just too warm in here !
Outer finally
You’ll notice that both the inner and the outer catch handlers have a go at handling the exception. The inner catch block catches the exception and re-throws it. The re-thrown exception is then subsequently caught and handled by the outer catch handler. Take note that you can throw exceptions from finally blocks too.
How the CLR uses the call-stack to locate a matching catch handler:
When an exception occurs, the CLR tries to locate an appropriate catch handler (associated with the current try block), which is capable of handling the exception. If it cannot find an appropriate catch handler, then the next outer try-catch block is examined for appropriate catch handlers. This search continues until it finds a matching catch handler within the scope of the currently executing method (in C#) / procedure (in
VB.NET). If it still cannot find a matching catch handler within the scope of the currently executing method/procedure, it pops the current method/procedure out of the call-stack thus causing the current method to lose scope, and then searches for matching catch handlers in the next method (the method that had originally called the current method/procedure) in the call-stack. If it cannot find a matching catch handler there too, it pops this method/procedure out and examines the next one in the call-stack. This stack unwinding continues until a matching catch handler is found for the exception that was thrown. If no such matching catch handler is found when the stack is completely unwound, then the exception becomes an unhandled exception.
Code listing in C#
using System;
// Rest of the code omitted for brevity...
class Diver
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Get Set Go...");
// Call the DiveIn() static method
Diver.DiveIn();
}
catch(SharkAttackException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In Main finally");
}
}
static void DiveIn()
{
try
{
// Call the DiveDeeper static method
Diver.DiveDeeper();
}
catch(WaterTooColdException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In DiveIn finally");
}
}
static void DiveDeeper()
{
try
{
throw new SharkAttackException("Two hungry Great-White sharks " + "on the prowl");
}
catch(OutOfOxygenException ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// This should always get called
Console.WriteLine("In DiveDeeper finally");
}
}
}
Code listing in VB.NETImports System
' Rest of the code omitted for brevity...
Module Diver
Sub Main()
Try
Console.WriteLine("Get Set Go...")
' Call the DiveIn() subroutine
Call DiveIn
Catch ex As SharkAttackException
Console.WriteLine(ex.Message)
Finally
' This should always get called
Console.WriteLine("In Main finally")
End Try
End Sub
Sub DiveIn()
Try
' Call the DiveDeeper() subroutine
Call DiveDeeper
Catch ex As WaterTooColdException
Console.WriteLine(ex.Message)
Finally
' This should always get called
Console.WriteLine("In DiveIn finally")
End Try
End Sub
Sub DiveDeeper()
Try
Throw New SharkAttackException("Two hungry Great-White sharks " + _
"on the prowl")
Catch ex As OutOfOxygenException
Console.WriteLine(ex.Message)
Finally
' This should always get called
Console.WriteLine("In DiveDeeper finally")
End Try
End Sub
End Module
Compile the application by running the following command from the DOS command line:
Compiling in C#
csc /target:exe Diver.cs
Compiling in VB.NET
vbc /target:exe Diver.vb
Run the program Diver.exe and observe the output:
Get Set Go...
In DiveDeeper finally
In DiveIn finally
Two hungry Great-White sharks on the prowl
In Main finally
In the above code fragment, the Main() entry point in the program calls the DiveIn() method (in C#) / Subroutine (in VB.NET), which in turn calls the DiveDeeper() method/subroutine. Notice that the DiveDeeper() method throws a custom application-defined exception called SharkAttackException.
Explanation using C#
Image
Explanation using VB.NET
Image
When the SharkAttackException exception is thrown the CLR checks to see if the catch handlers associated with the try block in DiveDeeper() can handle the exception Since the only exception type handled by the catch handler in DiveDeeper() happens to be OutOfOxygenException, the CLR will pop the DiveDeeper() method out of the stack after executing the finally block in DiveDeeper(). It will then go on to search for a suitable catch handler in the next method/procedure in the call-stack, which happens to be the DiveIn() method/subroutine. Since the try-catch block in the DiveIn() method also happens to have a catch handler that handles only WaterTooColdException exceptions, this method/procedure is also popped out of the call-stack after executing its finally block. The Main() method/subroutine, which is the next in the call-stack is then examined for matching catch handler within the try-catch block. As you can see, the try-catch block within the Main() method/subroutine does have a catch handler that can handle the SharkAttackException and so control is eventually passed over to this catch handler after which the corresponding finally block is executed. Assuming that the Main() entry-point method/subroutine did not have an appropriate catch handler too, then the next pop operation would have completely unwound the call-stack thereby making the SharkAttackException an unhandled exception
Catching arithmetic overflow exceptions with C#’s checked keyword:
Never ever discount the destructive effects that arithmetic overflow exceptions bring to
the stability of your software. For starters, recollect the tragic crash of the $7 billion
Ariane 5 rocket – A crash that resulted because its software system attempted to convert
a 64-bit floating-point number to a signed 16-bit integer, which subsequently caused an
overflow exception, and worse yet, there was no exception handler to handle this
exception. Sadly enough, the backup systems were also running on the same copy of the
software, without the exception handler. Read an account of the Ariane 5 crash at:
http://www.cs.ucsd.edu/users/goguen/courses/230/ariane5nyt.pdf
Let’s face it – How many times have we been in situations where we’ve stared in disbelief at our program spewing some insanely odd numerical output on arithmetic operations when it’s fairly obvious that the output is in not even slightly connected to what was expected of the program. Let’s quickly see what we mean here with an example. Consider the following C# program:
using System;
class ByteBites
{
static void Main(string[] args)
{
byte b1 = 57;
byte b2 = 200;
byte bResult = (byte)(b1 + b2);
Console.WriteLine("{0} + {1} = {2}", b1, b2, bResult);
}
}
Save this program to a file called ByteBites.cs. Compile the program using the following command from the DOS command line:csc /target:exe ByteBites.cs
Run ByteBites.exe and observe the output:
57 + 200 = 1
Many of us know what went wrong here. It’s fairly obvious here that a byte data type can hold only values from 0 to 255. Yet we are trying to add two bytes whose result over shoots the range of values that the resultant byte can hold, resulting in an overflow, and hence the absurd result 1. This is what the less wary among us (at least I do) run into when choosing data types to work with, paying little attention to the range of data that the program expects these data types to store and handle. A subtle arithmetic addition operation that has the potential to generate an overflow operation is enough to send your application to the tomb. Most often, good testing practices catch these bugs during the testing phase. But it certainly might get past the QA team if the test data being fed to the program is not very exhaustive and if every possible test case is not being taken into account – The Ariane 5 crash of 1996 is a testimony to that. So as developers, we need to
code defensively to catch such arithmetic overflow errors and handle them appropriately in the execution flow of the program as our application logic dictates, thus leaving no room for inconsistent or incorrect results to bring down the application to a grinding halt.
So let’s see where C# can help us here. C# provides the checked keyword to trap and handle such arithmetic overflows. When an arithmetic operation that is enclosed within a checked block (or checked expression) results in an overflow, the runtime generates a System.OverflowException. Compare this to our previous example where the overflow result was silently assigned to the resulting byte. So let’s modify the previous example to enclose the arithmetic addition of the two bytes within a checked block. The modified
code is shown below:
using System;
class ByteBites
{
static void Main(string[] args)
{
try
{
checked
{
byte b1 = 57;
byte b2 = 200;
byte bResult = (byte)(b1 + b2);
Console.WriteLine("{0} + {1} = {2}", b1, b2, bResult);
}
}
catch(OverflowException exOverflow)
{
Console.WriteLine("Caught overflow exception: {0}",
exOverflow.Message);
}
}
}
You’ll notice that we now have a checked block that encloses the arithmetic operation, which in turn is enclosed within a try block. Compile and run the application. Notice the output:
Caught overflow exception: Arithmetic operation resulted in an overflow.
You’ll notice that the addition operation generated a System.OverflowException because it was enclosed within a checked block. Remove the checked block and you’ll notice that you’ll again get back the cryptic 1 as the result. But then, wouldn’t it be asking too much if we had to put each and every arithmetic operation that had the potential to generate an overflow within a checked block. Thankfully, there’s an easier way to turn on arithmetic overflow checking for the entire application by using the /checked compiler option when
compiling your application. To test this, go ahead and remove the checked block that is enclosing the addition operation. This time compile the program with the /checked switch turned on, by typing the following command in the DOS command line:
csc /target:exe /checked ByteBites.cs
Run the program and observe the output that the program spews out:
Caught overflow exception: Arithmetic operation resulted in an overflow.
Notice that the /checked option has the same effect as using the checked block around your arithmetic operations. This option thereby allows you to enforce arithmetic overflow checks and to catch such exceptions throughout your application. So what if you’ve turned on the /checked option and want to selectively prevent certain parts in your application from generating an overflow exception when an overflow occurs. For example, assume you have a scenario where you need the check the value of the overflowed result to determine what action to take and so on. In such cases, you can use the unchecked keyword and enclose those arithmetic operations within an unchecked block so that an OverflowException is not generated for those operations. This is shown in the snippet of code below:
unchecked
{
byte b1 = 57;
byte b2 = 200;
byte bResult = (byte)(b1 + b2);
Console.WriteLine("{0} + {1} = {2}", b1, b2, bResult);
}
Of course, the above code fragment gives you yet another opportunity to see the infamous 1 as the result.
Using the checked keyword and /checked compiler option judiciously in your C# applications can help you catch arithmetic overflow exceptions and to ensure that your application stays sane.