Previous Page
Next Page

Throwing Exceptions

Suppose you are implementing a method called monthName that accepts a single int argument and returns the name of the corresponding month. For example, monthName(1) returns “January.” The question is: what should the method return when the integer argument is less than 1 or greater than 12? The best answer is that the method shouldn't return anything at all; it should throw an exception. The .NET Framework class libraries contain lots of exception classes specifically designed for situations such as this. Most of the time, you will find that one of these classes describes your exceptional condition. (If not, you can easily create your own exception class, but you need to know a bit more about the C# language before you can do that.) In this case, the existing .NET Framework ArgumentOutOfRangeException class is just right:

public static string monthName(int month) 
{ 
   switch (month) 
   { 
      case 1 :  
          return "January"; 
      case 2 : 
          return "February";  
      ... 
      case 12 : 
          return "December";  
      default : 
          throw new ArgumentOutOfRangeException("Bad month"); 
   } 
}

Notice how the default case uses a throw statement to generate an exception. The throw statement needs an exception to throw. This example uses an expression that creates a new ArgumentOutOfRangeException object. The object is initialized with a string that will populate its Message property, by using a constructor. Constructors are covered in detail in Chapter 7, “Creating and Managing Classes and Objects.”

In the following exercises, you will add code that throws and catches exceptions to the MathsOperators project.

Throw your own exception
  1. Return to Visual Studio 2005, and make sure the MathsOperators solution is still open.

  2. On the Debug menu, click Start Without Debugging.

  3. Type 24 in the left operand text box, type 36 in the right operand text box, and then click Calculate.

    The value 0 ppears in the Result text box. The fact that you have not selected an operator option is not immediately obvious. It would be useful to write a diagnostic message in the Result text box.

  4. Click Quit to return to the Visual Studio 2005 programming environment.

  5. In the Code pane displaying Form1.cs, locate and examine the doCalculation method. It looks like this:

    private int doCalculation(int leftHandSide, int rightHandSide) 
    { 
        int res = 0; 
        if (addition.Checked) 
            res = addValues(leftHandSide, rightHandSide); 
        else if (subtraction.Checked) 
            res = subtractValues(leftHandSide, rightHandSide); 
        else if (multiplication.Checked) 
            res = multiplyValues(leftHandSide, rightHandSide); 
        else if (division.Checked) 
            res = divideValues(leftHandSide, rightHandSide); 
        else if (remainder.Checked) 
            res = remainderValues(leftHandSide, rightHandSide); 
        return res; 
    }

    The addition, subtraction, multiplication, division, and remainder fields are the radio buttons that appear in the Operators group on the form. Each radio button has a Checked Boolean property that has the value true if the option is selected. The cascading if statement examines each radio button in turn to find out which one is selected. If none of the options are selected, none of the if statements will be true and the res variable will remain at its initial value (0). This variable holds the value that is returned by the method.

    IMPORTANT
    Do not confuse the C# checked keyword with the Checked property of a radio button—they are not related in any way.

    You could try to solve the problem by adding one more else statement to the if-else cascade, to write a message to the result text box, as follows:

    if (addition.Checked) 
        res = addValues(leftHandSide, rightHandSide); 
    ... 
    else if (remainder.Checked) 
        res = remainderValues(leftHandSide, rightHandSide); 
    else 
        result.Text = "no operator selected";

    However, this solution is not a good idea as it is not really the purpose of this method to output messages. With this code, you would have two methods in the program that write diagnostic messages to the result text box— calculate_Click and doCalculation. It is better to separate the detection and signaling of an error from the catching and handling of that error.

  6. Add one more else statement to the list of if-else statements (immediately before the return statement) and throw an InvalidOperationException exactly as follows:

    else 
        throw new InvalidOperationException("no operator selected");
  7. On the Debug menu, click Start Without Debugging to build and run the application.

  8. Type 24 in the left operand text box, type 36 in the right operand text box, and then click Calculate.

    An Exception message box appears.

  9. Click Details.

    The message tells you that an InvalidOperationException has been thrown with the string “no operator selected.”

    Graphic
  10. Click Quit in the Exceptions message box.

    The application terminates and you return to Visual Studio 2005.

Now that you have written a throw statement and verified that it throws an exception, you will write a catch handler to catch this exception.

Catch your own exception
  1. In the Code pane displaying Form1.cs, locate the calculate_Click method.

  2. Add the following catch handler immediately below the existing two catch handlers in the calculate_Click method:

    catch (InvalidOperationException ioEx) 
    { 
        result.Text = ioEx.Message; 
    }

    This code will catch the InvalidOperationException that is thrown when no operator option is selected.

  3. On the Debug menu, click Start Without Debugging.

  4. Type 24 in the left operand text box, type 36 in the right operand text box, and then click Calculate.

    The message “no operator selected” appears in the Result text box.

  5. Click Quit.

    The application is now a lot more robust than it was. However, several exceptions could still arise that would not be caught and might cause the application to fail. For example, if you attempt to divide by zero, an unhandled DivideByZeroException will be thrown (integer division by zero does throw an exception, unlike floating point division by zero). One way to solve this would be to write an ever larger number of catch handlers inside the calculate_Click method. However, a better solution would be to add a general catch handler that catches Exception to the end of the list of catch handlers. This will trap all unhandled exceptions.

    TIP
    The decision of whether to explicitly catch all unhandled exceptions in a method depends on the nature of the application you are building. In some cases, it makes sense to catch exceptions as close to the point that they occur as possible, but in other situations it is more useful to let an exception propagate back up the method call stack to be handled by the method that invoked the routine that threw the exception.
  6. In the Code pane displaying Form1.cs, locate the calculate_Click method.

  7. Add the following catch handler to the end of the list of existing catch handlers:

    catch (Exception ex) 
    { 
        result.Text = ex.Message; 
    }

    This catch handler will catch all hitherto unhandled exceptions, whatever their specific type.

  8. On the Debug menu, click Start Without Debugging.

    You will now attempt some calculations known to cause exceptions and confirm that they are all caught.

  9. Type 24 in the left operand text box, type 36 in the right operand text box, and then click Calculate. Confirm that the diagnostic message “no operator selected” still appears in the Result text box. This message was generated by the InvalidOperationException handler.

  10. Type John in the left operand text box, and then click Calculate. Confirm that the diagnostic message “Input string was not in a correct format” appears in the Result text box. This message was generated by the FormatException handler.

  11. Type 24 in the left operand text box, type 0 in the right operand text box, select the Divide option under Operators, and then click Calculate. Confirm that the diagnostic message “Attempted to divide by zero” appears in the Result text box. This message was generated by the general Exception handler.

  12. Click Quit.


Previous Page
Next Page