© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
G. ByrneTarget C#https://doi.org/10.1007/978-1-4842-8619-7_17

17. Exception Handling

Gerard Byrne1  
(1)
Belfast, Ireland
 

Exceptions

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.

A window with a series of codes. The line code for the message exception and the condition for the error code execution are highlighted.

Figure 17-1

FileNotFoundException – filename is incorrect

  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.

A window with a 6-line code. The line code for executing an error message for index out of bounds condition is highlighted.

Figure 17-2

Exception – index out of bounds

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.

A window with a series of codes. A line code for performing a calculation for division with an exception is highlighted.

Figure 17-3

Exception – divide-by-zero exception

  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 try block , 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 finally block , 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 throw keyword 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. 1.

    Right-click the solution CoreCSharp.

     
  2. 2.

    Choose Add.

     
  3. 3.

    Choose New Project.

     
  4. 4.

    Choose Console App from the listed templates that appear.

     
  5. 5.

    Click the Next button.

     
  6. 6.

    Name the project Chapter17 and leave it in the same location.

     
  7. 7.

    Click the Next button.

     
  8. 8.

    Choose the framework to be used, which in our projects will be .NET 6.0 or higher.

     
  9. 9.

    Click the Create button.

     
Now we should see the Chapter17 project within the solution called CoreCSharp .
  1. 10.

    Right-click the project Chapter17 in the Solution Explorer panel.

     
  2. 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 .
  1. 12.

    Right-click the Program.cs file in the Solution Explorer window.

     
  2. 13.

    Choose Rename.

     
  3. 14.

    Change the name to ExceptionHandling.cs.

     
  4. 15.

    Press the Enter key.

     
  5. 16.

    Double-click the ExceptionHandling.cs file to open it in the editor window.

     
  6. 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.

A window with a series of codes. The curly brace symbol for a try block line code is highlighted.

Figure 17-4

Try block needs a catch or 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.
  1. 18.

    Amend the code, as in Listing 17-5.

     
    static void Main(string[] args)
    {
      try
      {
        int hardwareTypeValue = 0;
        double premium = 100 / hardwareTypeValue;
        Console.WriteLine(premium);
      } // End of try block
      catch
      {
        Console.WriteLine("Error - you cannot divide by zero");
      } // End of catch block
    } //End of Main() method
Listing 17-5

Adding the catch block

  1. 19.

    Click the File menu.

     
  2. 20.

    Choose Save All.

     
  3. 21.

    Click the Debug menu.

     
  4. 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.

A console window with a series of codes. A line code for executing an error message when dividing with zero value is exhibited.

Figure 17-5

Catch block executes.

  1. 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.
  1. 24.

    Amend the code, as in Listing 17-6.

     
  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
    } //End of Main() method
Listing 17-6

Catch method that accepts the exception as a parameter

  1. 25.

    Click the File menu.

     
  2. 26.

    Choose Save All.

     
  3. 27.

    Click the Debug menu.

     
  4. 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.

A console window with a series of codes. The exception message executed by the line code when dividing by zero is exhibited.

Figure 17-6

Exception message displayed

  1. 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.

    A window with a series of codes and an inset of a context menu option. The options and buttons for creating an exception class are depicted.

    Figure 17-7

    Methods and fields of the Exception class

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.
  1. 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.
  1. 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.

A window with a highlighted line code. The highlighted line code is for displaying the exception message when a catch is met.

Figure 17-8

Exception message displayed

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.
  1. 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.
  1. 33.

    Click the File menu.

     
  2. 34.

    Choose Save All.

     
  3. 35.

    Click the Debug menu.

     
  4. 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.

A console window with a series of codes. An inset of the code specifying the exact exception message when a catch block is met is exhibited.

Figure 17-9

First exception clause executed rather than the general exception

  1. 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.
  1. 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

  1. 39.

    Click the File menu.

     
  2. 40.

    Choose Save All.

     
  3. 41.

    Click the Debug menu.

     
  4. 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.

A console window with a series of codes. The exception message and the line code for executing it when the catch block is met are exhibited.

Figure 17-10

General exception for file not found

  1. 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.
  1. 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

  1. 45.

    Click the File menu.

     
  2. 46.

    Choose Save All.

     
  3. 47.

    Click the Debug menu.

     
  4. 48.

    Choose Start Without Debugging.

     

A window with a series of codes. A line code for executing an exception message display is exhibited if a file is not found.

Figure 17-11

Specific FileNotFoundException message displayed

  1. 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

A window with a series of codes. A segment of a line code for exception message property is displayed.

Figure 17-12

FileNotFoundException.Message property message

  1. 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.
  1. 51.

    Amend the code, as in Listing 17-12, to add the finally block.

     
catch (Exception ex)
{
  Console.WriteLine($"Exception message is - {ex.Message}");
} // End of catch block
 finally
 {
   Console.WriteLine("Try catch blocks ended, tidying up");
 }
  } //End of Main() method
} // End of ExceptionHandling class
} // End of Chapter17 namespace
Listing 17-12

Using the finally block

  1. 52.

    Click the File menu.

     
  2. 53.

    Choose Save All.

     
  3. 54.

    Click the Debug menu.

     
  4. 55.

    Choose Start Without Debugging.

     
Figure 17-13 shows the console window, displaying at the end of the text the message from the finally block.

A window with a series of codes. The line code for displaying the message for finally block executed is exhibited.

Figure 17-13

Finally block executed and message appearing at the end

  1. 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}");
      }  // End of catch block
      finally
      {
        Console.WriteLine("Try catch blocks ended, tidying up");
        myStreamReader.Close();
      }
    } //End of Main() method
  } // End of ExceptionHandling class
} // End of Chapter17 namespace
Listing 17-13

Using the finally block to use the Close() method

StackTrace

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.
  1. 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

  1. 58.

    Click the File menu.

     
  2. 59.

    Choose Save All.

     
  3. 60.

    Click the Debug menu.

     
  4. 61.

    Choose Start Without Debugging.

     
  1. 62.

    Press the Enter key to close the console window.

     

A window filled with the exception message. This is also known as the stack trace message.

Figure 17-14

Stack trace message

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.
  1. 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.
  1. 64.

    Amend the code, as in Listing 17-16.

     
} //End of Main() method
public static void CheckIfQuoteCanBeMade()
{
  Console.WriteLine("What is the value of the hardware to be insured?");
double hardwareValue = Convert.ToDouble(Console.ReadLine());
 try
 {
   if (hardwareValue > 0 && hardwareValue < 10000)
   {
     Console.WriteLine("Quote will be available");
   }
   else
   {
     throw (new HardwareValueException("HardwareValueException - value too high"));
    }
 } // End of try block
   catch (HardwareValueException ourException)
   {
     Console.WriteLine(ourException.Message.ToString());
   } // End of  catch block
 }// End of CheckIfQuoteCanBeMade method
  } // End of ExceptionHandling class
Listing 17-16

New method inside the ExceptionHandling class outside Main()

  1. 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()

  1. 66.

    Click the File menu.

     
  2. 67.

    Choose Save All.

     
  3. 68.

    Click the Debug menu.

     
  4. 69.

    Choose Start Without Debugging.

     
  5. 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.

A window with a 2-line code. The code written is for executing a message when a high value inputted is too high.

Figure 17-15

Message from custom HardwareValueException

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. 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.
  1. 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?");
    hardwareValue = Convert.ToDouble(Console.ReadLine());
   }
   catch (Exception)
   {
     throw;
   }
 } // End of GetHardwareValue() method
  } // End of ExceptionHandling class
Listing 17-19

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

  1. 3.

    Click the File menu.

     
  2. 4.

    Choose Save All.

     
  3. 5.

    Click the Debug menu.

     
  4. 6.

    Choose Start Without Debugging.

     
  5. 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.

A window with a 6-line code. The exception message displayed on the console window is exhibited.

Figure 17-16

Throw the exception up the hierarchy, rethrow.

  1. 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.
  1. 9.

    Right-click the Chapter17 project in the Solution Explorer window.

     
  2. 10.

    Choose Add

     
  3. 11.

    Choose New.

     
  4. 12.

    Choose Class

     
  5. 13.

    Change the name to ExceptionHandlingWithSwitch.cs.

     
  6. 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

  1. 15.

    Amend the code, as in Listing 17-21, to include a Main() method with the try block and the three catch blocks.

     
namespace Chapter17
{
internal class ExceptionHandlingWithSwitch
{
  static void Main(string[] args)
  {
    try
    {
    } // End of try block
    catch (Exception ex)
    {
      switch (ex)
      {
        case FileNotFoundException:
          Console.WriteLine("File not found Exception");
          break;
        case DivideByZeroException:
          Console.WriteLine("Divide By Zero Exception");
          break;
        case OverflowException:
          Console.WriteLine("Overflow Exception");
          break;
      } // End of switch block
    } // End of catch block
    finally
    {
      Console.WriteLine("Try catch blocks ended, tidying up");
    } // End of finally block
  } // End of Main() method
} // End of ExceptionHandlingWithSwitch class
} // End of Chapter17 namespace
Listing 17-21

Switch statement to encompass different exceptions

  1. 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

  1. 17.

    Right-click the Chapter17 project in the Solution Explorer panel.

     
  2. 18.

    Choose Properties.

     
  3. 19.

    Set the Startup object to be the ExceptionHandlingWithSwitch in the drop-down list.

     
  4. 20.

    Close the Properties window.

     
  5. 21.

    Click the File menu.

     
  6. 22.

    Choose Save All.

     
  7. 23.

    Click the Debug menu.

     
  8. 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.

A window with a 3-line code. The relevant exception if a file is not found catch is displayed.

Figure 17-17

FileNotFoundException

  1. 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.
  1. 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

  1. 27.

    Click the File menu.

     
  2. 28.

    Choose Save All.

     
  3. 29.

    Click the Debug menu.

     
  4. 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 .

A console window displays the line code for an exception when dividing by zero.

Figure 17-18

DivideByZeroException

  1. 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.
  1. 32.

    Remove the code from the previous steps, as in Listing 17-24.

     
    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;
        // Testing OverflowException
        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

  1. 33.

    Click the File menu.

     
  2. 34.

    Choose Save All.

     
  3. 35.

    Click the Debug menu.

     
  4. 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.

A console window displays the line code for a question and the overflow exception when the value entered is over 300.

Figure 17-19

OverflowException

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.

An illustration of a series of a layer of concentric circles. It consists of text, our target is getting closer is written below, which is written below the outermost circle.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.31.26