In the previous chapter on file handling, we gained knowledge of different classes and methods that could be used to read from and write to a file. More importantly, when we used the methods CopyAFile()
, DeleteAFile()
, and UseStreamWriterRead()
, we introduced the concept of the try catch block. In this chapter we will enhance our knowledge and skills in exception handling.
What Is an Exception?
We should think of an exception as an “exceptional event
” that occurs when an application program is being executed. If we think about a time when we have seen an unhandled exception
in our personal life, we will understand the consequences of such exception. What about when we have been dependent on information from a screen at an airport or railway station and the screen displayed a technical message of the unhandled exception? This may be an inconvenience to us, but thankfully it is unlikely to cause us any serious damage. However, what if we required the use of an emergency device
in our home and the device screen displayed an exception! This situation may be more inconvenient or indeed life-threatening. Unfortunately, when we develop code
, there are many different things within our code that can cause an exception. There can also be exceptions outside our code that will cause our code to stop working and are outside our control. Some examples of exceptions that we might encounter are the following:
When the code
is required to read a text file, as in Listing 17-1, but the text file is corrupted or not at the location, we get a FileNotFoundException. Figure 17-1 shows the exception when the file is not found because the filename has been entered as policydetail.txt instead of policydetails.txt.
internal class Exceptions
{
static void Main(string[] args)
{
StreamReader myStreamReader = new
StreamReader("policydetail.txt");
while (myStreamReader.EndOfStream)
{
Console.Write(myStreamReader.Read() + " ");
}
myStreamReader.Close();
} // End of Main() class
} // End of Exceptions class
Listing 17-1
Code will cause a FileNotFoundException
When the code is required to read a database and the SQL server returns a SQL exception.
When iterating an array and trying to go past the last item, we get an IndexOutOfRangeException
, or out-of-bounds exception. Listing 17-2 shows code that gives an IndexOutOfRangeException as shown in Figure 17-2.
static void Main(string[] args)
{
string[] hardwareTypes = new String[3];
hardwareTypes[0] = "Laptop";
hardwareTypes[1] = "Desktop";
hardwareTypes[2] = "Printer";
for (int counter = 0; counter < 4; counter++)
{
Console.WriteLine($"The hardware type is
{hardwareTypes[counter]}");
}
} // End of Main() class
} // End of Exceptions class
Listing 17-2
Code will cause an IndexOutOfRangeException
Performing a calculation
involving division that is dependent on the user input for the divisor. When a 0 is input by the user, the calculation will cause a DivideByZeroException. Listing 17-3 shows code that gives a divide-by-zero exception as shown in Figure 17-3.
internal class Exceptions
{
static void Main(string[] args)
{
int hardwareTypeValue = 0;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
} // End of Main() class
} // End of Exceptions class
Listing 17-3
Code will cause a DivideByZeroException
When we get a .NET exception, it is an object that holds information about the specific error. We can then access the object within our code and display whatever information we require.
When we code, we need to use exception handling when there is the possibility of an error happening, and we can use a number of different “tools
,” which include
The tryblock
, which is used to segment code that can cause an exception.
The catch block(s), which is associated with the try block and handles the caught exception. This is an optional block if we have the try and finally blocks.
A finallyblock
, where we write code that will execute whether there is a caught exception or not. This is an optional block if we have the try and catch blocks.
We will now look in more detail at the try, catch, and finally blocks of code.
try
The try block
will contain C# code between open and close curly braces {}. The reason we would enclose lines of code within a try block is that we identify them as being capable of causing an exception. Obviously, when we have an exception, we must handle the exception, or the code will “crash.” In order to handle the exception caught in the try block, we must associate the try with a catch block. The syntax for the try block will
be similar to
try
{
// code statements that may cause an exception
}
catch
The catch block
will contain C# code between open and close curly braces {}. The code will be used to handle the caught exception and could include displaying the details from the error object, for example, the stack trace or specific error message. We can have one catch block or more, but if we have only one catch block, then it can be used to handle all exceptions. If we have more than one catch block, when the try block has an error, it will be handled by the appropriate catch block. Looking back at our common exceptions, we might have a first catch block for a DivideByZeroException and a second one for a FileNotFoundException and so on. When the try block tries to divide by zero, then the first catch block will be executed:
Int numberOne = 5, numberTwo = 0;
try
{
double answer = numberOne / numberTwo;
}
catch (DivideByZeroException)
{
// code statements that handle DivideByZeroException
Console.WriteLine(“DivideByZeroException”);
}
catch (FileNotFoundException)
{
// code statements that handle FileNotFoundException
// Console.WriteLine(“FileNotFoundException”);
}
The important thing to remember about catch is that it can be used without accepting arguments, catch, and this means it will catch any type of exception. But the recommended usage is catch(), and the argument contains the exception details, and this is how we can use the exception details to display a message. Every exception inherits from SystemException
, and therefore we can have a catch block that appears as catch(Exception). This Exception will therefore catch any exceptions not previously found. In this format the exception is not held in a variable and therefore cannot be used. When we use multiple catch blocks, we should always have a “fallback
” for an exception we have not previously tried to catch. Amending the code for the previous example by assigning different values for numberOne and numberTwo and adding an array, when the code would try to read the array value outside the range, we will invoke the generic exception, catch(Exception):
int numberOne = 50, numberTwo = 10;
int[] claims = { 100, 200, 300 };
try
{
double answer = numberOne / numberTwo;
Console.WriteLine(claims[3]);
}
catch (DivideByZeroException)
{
// code statements that handle DivideByZeroException
Console.WriteLine("DivideByZeroException");
}
catch (FileNotFoundException)
{
// code statements that handle FileNotFoundException
Console.WriteLine("FileNotFoundException");
}
catch (Exception)
{
Console.WriteLine(“An exception was found”);
}
finally
The finally block
will contain C# code between open and close curly braces {}. The code will be used to execute whatever we wish to happen regardless of whether there was an exception or not. In other words, the finally block will always be executed, whereas the catch block is only executed if there is an exception it can handle. The syntax for the finally block will be similar to
finally
{
// code statements that will be executed if there is an
exception or if there is no exception
}
throw
When exceptions occur and are handled by the try, catch, and finally blocks, we are handling exceptions that have been thrown by the system at runtime. However, what would happen if we wanted to throw an exception manually in our code? Well, this is where the throwkeyword
comes to our assistance, and we will look at this concept later.
Now we will implement our theory of exceptions
that we have just read, by creating some code.
Let’s code some C# and build our programming muscle.
Add a new project to hold the code for this chapter.
1.
Right-click the solution CoreCSharp.
2.
Choose Add.
3.
Choose New Project.
4.
Choose Console App from the listed templates that appear.
5.
Click the Next button.
6.
Name the project Chapter17 and leave it in the same location.
7.
Click the Next button.
8.
Choose the framework to be used, which in our projects will be .NET 6.0 or higher.
9.
Click the Create button.
Now we should see the Chapter17 project within the solution called CoreCSharp
.
10.
Right-click the project Chapter17 in the Solution Explorer panel.
11.
Click the Set as Startup Project option.
Notice how the Chapter17 project name has been made to have bold text, indicating that it is the new startup project and that it is the Program.cs file within it that will be executed when we run the debugging
.
12.
Right-click the Program.cs file in the Solution Explorer window.
13.
Choose Rename.
14.
Change the name to ExceptionHandling.cs.
15.
Press the Enter key.
16.
Double-click the ExceptionHandling.cs file to open it in the editor window.
17.
Amend the code, as in Listing 17-4, with the namespace, class, and Main() method and a try block with some code.
namespace Chapter17
{
internal class ExceptionHandling
{
static void Main(string[] args)
{
try
{
int hardwareTypeValue = 0;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
} // End of try block
} //End of Main() method
} // End of ExceptionHandling class
} // End of Chapter17 namespace
Listing 17-4
Class with the Main() method
and try block
The closing curly brace may be showing a red underline, as Figure 17-4, indicating an error, and hovering over the error
would display a message that tells us we must use either a matching catch block or a finally block.
If we think about this, it makes sense, because why would we try to find an exception and then do nothing about the exception? Not handling exceptions is a root cause for unreliable software applications, which cause business revenue losses and make the user experience unsatisfactory.
We will now create the catch block and add some code to handle any exception. Here we will simply display a message.
Console.WriteLine("Error - you cannot divide by zero");
} // End of catch block
} //End of Main() method
Listing 17-5
Adding the catch block
19.
Click the File menu.
20.
Choose Save All.
21.
Click the Debug menu.
22.
Choose Start Without Debugging.
Figure 17-5 shows the console window
displaying the exception message we have coded, when we handled the exception through our catch block.
23.
Press the Enter key to close the console window.
Great, as the developer, we have caught the exception and “gracefully” handled it using the catch block, and our application can move to the next statements. But what was the actual exception? Well, we used the catch that accepted no arguments, and we displayed a message of our choice. We could have used the preferred catch option, which lets the catch accept an argument passed to it. In other words, the actual exception is passed and accepted as the parameter of the catch, and this will be more beneficial to us as we will be getting more details about the exception. In accepting the exception as its parameter, the catch will have been coded to assign the exception a name, and this could be ex or anything we would like to call it. In this example we will use ex, not a great name when we think about clean code, but we will discuss this issue later in the chapter.
We will now replace the existing catch block so that we have a new catch block that accepts the passed-in argument as its parameter and add some code to display the message that belongs to the exception.
Console.WriteLine($"Exception message is - {ex.Message}");
} // End of catch block
} //End of Main() method
Listing 17-6
Catch method that accepts the exception as a parameter
25.
Click the File menu.
26.
Choose Save All.
27.
Click the Debug menu.
28.
Choose Start Without Debugging.
Figure 17-6 shows the console window
, which displays the exception message, which was passed from the actual system exception, which we called ex. We should notice that the message passed from the exception handler, Attempted to divide by zero, is similar to what we manually entered in our catch code. The important thing is that we can use the existing exception messages rather than having to create our own messages.
29.
Press the Enter key to close the console window.
Great, we have caught the exception and displayed the actual exception message, which came from the “generic” exception. But we could also catch more specific exceptions like the divide-by-zero exception, and we can use the general and specific together.
Multiple Exceptions
We can have more than one exception
handler if we wish to and be more specific about the exception. In Listing 17-6 we saw that the catch accepted the exception as its parameter called ex. We then used the Message field from the ex instance
in the console message using the code line
Console.WriteLine($"Exception message is - {ex.Message}");
While this was fine and the code worked, there are a couple of points to consider:
Is it acceptable to call the parameter ex? What about clean code?
Let's be honest. ex is not a very good name for a variable or parameter. What does ex tell us about its meaning or purpose? We have already read about clean code
and self-documenting code
, so maybe we should consider being more explicit about the name. Certainly, if we search for code snippets or solutions to problems on the World Wide Web, we will see the naming convention ex, but remember we must try and make our code more readable and more maintainable, not just for ourselves but for others who will have to work with our code. We can therefore be more explicit in our naming of the parameter, for example, exceptionFound, divideByZeroException, or fileNotFoundException.
What does the ex really mean?
As we have just said, ex is the parameter name and it is of type Exception. So, for simplicity and going back to Chapter 6, we could read the Exception ex as the variable ex of data type Exception. However, Exception is a class, and therefore we would say ex is the instance of the class or the object. The Exception class, like most classes, will be made up of methods and properties, and one of the properties is Message
, which gets a message that describes the current exception. This concept of fields and methods within a class should be familiar to us because we have coded our own examples of classes and objects in Chapter 13 and we used fields and methods within them. Looking at the Exception class from the example code shown in Figure 17-7, we can see some methods and fields that exist within it.
We will now look at multiple exceptions, where we can prioritize the possible exceptions, starting with the more specific and moving to the more general, which is really what the ex was in Listing 17-6. Here we will just use the name DivideByZeroException with no instance of it being made.
30.
Amend the code, as in Listing 17-7, to create another catch block that checks for the specific exception DivideByZeroException.
try
{
int hardwareTypeValue = 0;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
} // End of try block
catch (Exception ex)
{
Console.WriteLine($"Exception message is - {ex.Message}");
} // End of catch block
catch (DivideByZeroException)
{
Console.WriteLine($"Divide By Zero Exception message");
} // End of DivideByZeroException catch block
} //End of Main() method
Listing 17-7
Multiple catch clauses
In theory this seems a reasonable thing to do. We have two types of catch, but we have an error showing in our code.
31.
Hover over the red underline of the DivideByZeroException.
The error message
, as shown in Figure 17-8, tells us that the second catch is unnecessary as the first “generic” catch will already handle the divide-by-zero exception.
However, let us reverse the order of the catch blocks, as in Listing 17-8, putting the specific error first and then having the “generic” catch, and see what happens.
32.
Amend the code, as in Listing 17-8, to reverse the order of the catch() clauses.
} // End of try block
catch (DivideByZeroException)
{
Console.WriteLine($"Divide By Zero Exception message");
} // End of DivideByZeroException catch block
catch (Exception ex)
{
Console.WriteLine($"Exception message is - {ex.Message}");
} // End of catch block
} //End of Main() method
Listing 17-8
Multiple catch clauses – reversed
The red underline will have disappeared because we have “stacked” the catch blocks in a hierarchical manner from the specific to the general.
33.
Click the File menu.
34.
Choose Save All.
35.
Click the Debug menu.
36.
Choose Start Without Debugging.
Figure 17-9 shows the console window displaying the specific exception message, which we have created. This shows that there is precedence in the catch blocks.
37.
Press the Enter key to close the console window.
FileNotFoundException
We will now make a few changes
so we can see the effect of the catch blocks being checked in sequential order. We will change the value of the variable hardwareTypeValue, as in Listing 17-9, from a 0 to a 10, and we will use code very similar to code we used in Chapter 16 to read the data from a file. However, we will use a filename that does not exist, and this will mean there will be a file-not-found exception.
38.
Amend the code inside the try block, as in Listing 17-9, to have code that attempts to read a file.
try
{
int hardwareTypeValue = 10;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
StreamReader myStreamReader = new StreamReader("policydetailsXXX.txt");
while (myStreamReader.EndOfStream)
{
Console.Write(myStreamReader.Read() + " ");
}
myStreamReader.Close();
} // End of try block
Listing 17-9
hardwareTypeValue=10 and read a text file that does not exist
39.
Click the File menu.
40.
Choose Save All.
41.
Click the Debug menu.
42.
Choose Start Without Debugging.
We will see that the console window, as shown in Figure 17-10, now displays the general exception message. The first part of the message is the hard-coded text string, while the second part is the property from the ex instance
of the Exception class.
43.
Press the Enter key to close the console window.
Great, again we have caught the exception and displayed the “generic” exception message. But we could also catch the more specific exception like the file-not-found exception. We will now add a new catch block for the FileNotFoundException, coding it just before the “generic” catch block. Here we will use the name FileNotFoundException but we will use an instance of it, calling it exNoFile.
44.
Amend the code, as in Listing 17-10, to add the new catch block.
} // End of try block
catch (DivideByZeroException)
{
Console.WriteLine($"Divide By Zero Exception message");
} // End of DivideByZeroException catch block
catch (FileNotFoundException exNoFile)
{
// Write the 'whole' exception
Console.WriteLine(exNoFile);
} // End of FileNotFoundException catch block
catch (Exception ex)
{
Console.WriteLine($"Exception message is - {ex.Message}");
} // End of catch block
} //End of Main() method
Listing 17-10
Add catch block for FileNotFoundException
45.
Click the File menu.
46.
Choose Save All.
47.
Click the Debug menu.
48.
Choose Start Without Debugging.
49.
Press the Enter key to close the console window.
Figure 17-11 shows the console window
displaying the specific exception, which we have created for the file not found. In this example we have displayed the whole exception in the WriteLine() method
as we have simply used exNoFile, the instance of the Exception class, and this shows both the Message and StackTrace of the exception. In the example as shown in Figure 17-11, we used the ex.Message where Message was the property of the Exception class that gave us access to details about the cause of the exception. Figure 17-12 shows what we would see if we had used the code exNoFile.Message as in Listing 17-11.
catch (FileNotFoundException exNoFile)
{
// Write the exception Message property value
Console.WriteLine(exNoFile.Message);
}// End of FileNotFoundException catch block
Listing 17-11
Using the Message property of the exception
50.
Press the Enter key to close the console window.
finally
When handling exceptions, we can use a finally block
with either the try catch or a try. The finally block of code is used to execute whatever business logic there is, regardless of whether there was an exception or not. The finally block always gets executed.
51.
Amend the code, as in Listing 17-12, to add the finally block.
catch (Exception ex)
{
Console.WriteLine($"Exception message is - {ex.Message}");
Figure 17-13 shows the console window, displaying at the end of the text the message from the finally block.
56.
Press the Enter key to close the console window.
In one of our try blocks, we had code that was using the StreamReader
to read a text file and, as is good practice, we closed the instance of the StreamReader, myStreamReader, when we had finished using it. The finally block would also be a location to code the Close() method
, but the instance of the StreamReader would need to be moved to outside the try block. Otherwise, it would not be accessible. The example code for this finally block is shown in Listing 17-13, where we can see the instance of the StreamReader is moved to the class level and made static. We do not need to code this; it’s merely for information and for demonstrating another way to use the finally block.
using System;
namespace Chapter17
{
internal class ExceptionHandling
{static StreamReader myStreamReader = new StreamReader("policydetailsXXX.txt");
static void Main(string[] args)
{
try
{
int hardwareTypeValue = 10;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
while (myStreamReader.EndOfStream)
{
Console.Write(myStreamReader.Read() + " ");
}
myStreamReader.Close();
} // End of try block
catch (DivideByZeroException)
{
Console.WriteLine($"Divide By Zero Exception message");
} // End of DivideByZeroException catch block
catch (FileNotFoundException exNoFile)
{
// Write the 'whole' exception
Console.WriteLine(exNoFile);
}// End of FileNotFoundException catch block
catch (Exception ex)
{
Console.WriteLine($"Exception message is - {ex.Message}");
Just like we used the Message property of the Exception class, we could also have used the StackTrace property
, and we would get a different output to help us identify the exception cause. Using the code in Listing 17-14, we will get the exception message as shown in Figure 17-14. This is a more detailed message.
57.
Amend the code, as in Listing 17-14, to display the stack trace message.
catch (FileNotFoundException exNoFile)
{
// Write the StackTrace exception
Console.WriteLine(exNoFile.StackTrace);
}// End of FileNotFoundException catch block
Listing 17-14
Using the StackTrace property
58.
Click the File menu.
59.
Choose Save All.
60.
Click the Debug menu.
61.
Choose Start Without Debugging.
62.
Press the Enter key to close the console window.
throw
Rather than using the try, catch, and finally blocks to handle exceptions thrown at runtime, we can throw
an exception manually in our code. Any exception that is derived from the Exception class can be thrown by us within our code, and we can choose where in the code to throw the exception.
When we throw our new exception, the class it belongs to must be based on the Exception class. In this example we will create a class for a hardware value being too high, and inside it, we will create our new exception. The code is added inside the namespace but outside the ExceptionHandling class.
63.
Amend the code, as in Listing 17-15, to add a class for our new exception, which has to inherit from the Exception base class.
} //End of Main() method
} // End of ExceptionHandling class
// Our custom exception for a value which is too high
public class HardwareValueException : Exception
{
public HardwareValueException(string errormessage) :base(errormessage)
{
} // End of HardwareValueException constructor
} // End of HardwareValueException class
} // End of Chapter17 namespace
Listing 17-15
New class HardwareValueException
inside namespace
We will now use the newly created exception method, which accepts the user input for the hardware value and throws an exception if the value is too high.
New method inside the ExceptionHandling class outside Main()
65.
Amend the code, as in Listing 17-17, to call the new method, which contains the new exception handler.
static void Main(string[] args)
{
CheckIfQuoteCanBeMade();
try
{
int hardwareTypeValue = 10;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
Listing 17-17
New method inside the ExceptionHandling class outside Main()
66.
Click the File menu.
67.
Choose Save All.
68.
Click the Debug menu.
69.
Choose Start Without Debugging.
70.
Enter a value greater than 10000, for example, 20000.
We will see that the console window, as shown in Figure 17-15, displays the message from our custom exception.
rethrow
The throw keyword allows us to rethrow an exception and this is useful if we wish to pass an exception
to the caller that will then use the exception for its own purposes. In this example
We will call a method that will ask the user to input the value of the hardware that is to be insured.
The method call is within a try block.
The corresponding catch block will display the exception if there is one.
The exception to be displayed is the exception received from the catch block of the method being called.
The method passes back the exception to the calling try catch block; it is a hierarchical structure, and this is where we use the throw statement without anything else. Within the catch block of the exception, we could decide to perform some operations like writing the exception details to a log file or the console, and then we can rethrow the exception, which simply means calling the throw statement without anything else, no object. The rethrow can only be used within a catch block.
When we throw an exception, we can do it in one of two ways:
throw – Using throw on its own means the stack trace is “preserved.”
throw ex – Using the throw with the ex means the stack trace is lost.
Think about when the exceptions move up through what is the “call stack
.” As we go through the call stack, it would be great if the stack trace was maintained and the exception details were accumulated. Well, this is where the throw on its own works perfectly. In the same scenario, if we used the throw ex, then only the last exception stack trace would be available.
We will now create a method called GetHardwareValue()
and call it from within the Main() method. We will also comment out the previous call to the CheckIfQuoteCanBeMade() method
.
1.
Amend the code, as in Listing 17-18, to call the method within a try and comment out the other method call.
static void Main(string[] args)
{
try
{
GetHardwareValue();
}
catch (Exception ex)
{
/*
Do something here that is specific to our business logic.
*/
Console.WriteLine(ex.Message);
}
//CheckIfQuoteCanBeMade();
try
{
Listing 17-18
Call the method from within a try catch block
We will now create the new method that will ask the user to input a value for the hardware and it will convert the string input to a double. The conversion is within a try block and the catch block uses the throw statement to pass the exception back to the calling method.
2.
Amend the code, as in Listing 17-19, to create the method that reads the user value and tries to convert it within a try block.
} // End of catch block
}// End of CheckIfQuoteCanBeMade method
public static void GetHardwareValue()
{
double hardwareValue;
try
{
Console.WriteLine("What is the hardware replacement value?");
Create the method that converts the input from within a try catch block
Notice the throw statement; this is the rethrow that will pass the exception that has been found back to the catch block belonging to the calling method. For simplicity we will comment out the code for the while iteration that reads the text file, as in Listing 17-20.
try
{
int hardwareTypeValue = 10;
double premium = 100 / hardwareTypeValue;
Console.WriteLine(premium);
//StreamReader myStreamReader = new
// StreamReader("policydetailsXXX.txt");
//while (myStreamReader.Peek() > 0)
//{
// Console.Write(myStreamReader.Read() + " ");
//}
//myStreamReader.Close();
} // End of try block
catch (DivideByZeroException)
Listing 17-20
Commenting out the code for the while iteration
3.
Click the File menu.
4.
Choose Save All.
5.
Click the Debug menu.
6.
Choose Start Without Debugging.
7.
Enter a string value instead of a numeric value for the replacement value, for example, Two Thousand.
We will see that the console window, as shown in Figure 17-16, displays the exception, which was passed up from the exception in the method that was called.
8.
Press the Enter key to close the console window.
Another way to code the multiple exceptions we have would be to enclose the exceptions within a switch construct
, which we looked at in Chapter 9 on selection. The example we will now code uses the switch construct to handle one of three exception types.
9.
Right-click the Chapter17 project in the Solution Explorer window.
10.
Choose Add
11.
Choose New.
12.
Choose Class
13.
Change the name to ExceptionHandlingWithSwitch.cs.
14.
Press the Enter key.
The ExceptionHandlingWithSwitch class code
will appear in the editor window, and we will now add
A Main() method
A try block with no code for now
A catch block for a FileNotFoundException
A catch block for a DivideByZeroException
A catch block for a OverflowException
A finally block
15.
Amend the code, as in Listing 17-21, to include a Main() method with the try block and the three catch blocks.
Switch statement
to encompass different exceptions
16.
Amend the code, as in Listing 17-22, to include code within the try block, which will attempt to open a file that does not exist.
internal class ExceptionHandlingWithSwitch
{
static void Main(string[] args)
{
try
{
// Testing FileNotFoundException
using (var fileStream = new FileStream(@"NoFileExists.txt", FileMode.Open))
{
// Logic for reading file would go here
}
} // End of try block
catch (Exception ex)
{
switch (ex)
Listing 17-22
Try to read a file that does not exist
17.
Right-click the Chapter17 project in the Solution Explorer panel.
18.
Choose Properties.
19.
Set the Startup object to be the ExceptionHandlingWithSwitch in the drop-down list.
20.
Close the Properties window.
21.
Click the File menu.
22.
Choose Save All.
23.
Click the Debug menu.
24.
Choose Start Without Debugging.
We will see that the console window, as shown in Figure 17-17, displays the relevant exception, which is FileNotFoundException.
25.
Press the Enter key to close the console window.
We will now comment the code we have just entered for reading the file so we can add code that will cause a DivideByZeroException.
26.
Amend the code, as in Listing 17-23, to comment the first piece of code and then include code within the try block that will attempt to perform a division by zero.
internal class ExceptionHandlingWithSwitch
{
static void Main(string[] args)
{
try
{
// Testing FileNotFoundException
//using (var fileStream = new FileStream(@"NoFileExists.txt", FileMode.Open))
//{
// // Logic for reading file would go here
//}
// Testing DivideByZeroException
int hardwareTypeValue = 0;
double premium = 100 / hardwareTypeValue;
} // End of try block
Listing 17-23
Try to perform a division by zero
27.
Click the File menu.
28.
Choose Save All.
29.
Click the Debug menu.
30.
Choose Start Without Debugging.
We will see that the console window, as shown in Figure 17-18, displays the relevant exception, which is DivideByZeroException
.
31.
Press the Enter key to close the console window.
We will comment the code we entered for the division by zero. Then we can add code within the try block to ask the user for a byte value input, and this will cause an exception when the user enters a value greater than 255.
32.
Remove the code from the previous steps, as in Listing 17-24.
Console.WriteLine("How many claims are being made?");
int claimValue = Convert.ToByte(Console.ReadLine());
} // End of try block
Listing 17-24
Try to enter a number greater than 255
33.
Click the File menu.
34.
Choose Save All.
35.
Click the Debug menu.
36.
Choose Start Without Debugging.
We will see that the console window, as shown in Figure 17-19, displays the question, and when we enter 300 and press the Enter key, the OverflowException is displayed.
This example should help us see how we can build our code using the constructs we have learned from the start of the chapters. It is important we think differently to make our code “cleaner” and easier to maintain.
Chapter Summary
So, finishing this chapter on exceptions, we have learned that
An exception is derived from the System.Exception class.
With a try block, we must have at least a matching catch or finally block.
We can have multiple catch blocks.
With multiple catch blocks, there is a hierarchy, and we should code them from the specific to the general.
We can have a simple catch that accepts no arguments and it will handle any exception.
We can have a catch that accepts an instance of the Exception and use its properties such as Message and StackTrace to get more details about the exception.
We can have a finally block, which is always executed.
We can throw an exception.
We can create our own user-defined exception.
We can rethrow an exception.
We are now really beginning to think like professional developers, we are considering that exceptions can occur, and we know one technique to handle the exceptions.
We are making fantastic progress in programming our C# applications and we should be very proud of our achievements. In finishing this chapter, we have increased our knowledge further and we are advancing to our target.