13.1 Exception examples


Three examples are given here as a refresher:

  • basic try-catch-finally ;

  • nested try;

  • user -defined exception.

Read through these examples to familiarize yourself with the different types of exception objects thrown for each scenario. Note the comments I have made at the end of each example.

13.1.1 Exception example 1: basic try-catch-finally

Here is a basic example of exception handling in C#. Try to trace the program flow based on your knowledge of Java's exception handling.

 1: using System;  2:  3: class TestClass{  4:   public static void Main(){  5:  6:     Console.Write("Enter a number :");  7:     string userInput = Console.ReadLine();  8:  9:     try{ 10:       int number1 = Convert.ToInt32(userInput); 11:       int number2 = 1/number1; 12:     } 13:     catch (FormatException e){ 14:       Console.WriteLine("check point 1"); 15:       Console.WriteLine(e.Message); 16:       Console.WriteLine(e.StackTrace); 17:     } 18:     catch (DivideByZeroException e){ 19:       Console.WriteLine("check point 2"); 20:       Console.WriteLine(e.Message); 21:       Console.WriteLine(e.StackTrace); 22:     } 23:     finally { 24:       Console.WriteLine("running finally block"); 25:     } 26:   } 27: } 

Convert.ToInt32() on line 10 is a static method of the System.Convert class. Like Java's Integer.parseInt() , Convert.ToInt32() takes in a string and returns an int . While Integer.parseInt() throws a java.lang.NumberFormatException if the string passed in contains non-numeric characters , Convert.ToInt32() throws a System.FormatException when that happens.

Line 11 may throw a System.DivideByZeroException if a division by 0 is performed. As the name implies, System.DivideByZeroException is the C# equivalent of java.lang.ArithmeticException .

You can execute the program, key in different inputs, observe the output, and trace through the code.

First case: enter a non-numeric string
 c:\expt>test Enter a number:  not_a_number  check point 1 Input string was not in a correct format at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info) at TestClass.Main() running finally block 

This user input will cause line 10 to throw a System.FormatException . The exception is caught at the first catch block (line 13). The finally block (line 23) runs after that. The program flow goes like this:

  • lines 5 “ 10 (exception object created on line 10);

  • lines 13 “ 17 (catch block);

  • lines 23 “ 25 (finally block).

Second case: enter the number zero
 c:\expt>test Enter a number:   check point 2 Attempted to divide by zero at TestClass.Main() running finally block 

The statement on line 11 throws a System.DivideByZeroException exception. Similarly, the finally block runs after the second exception block finishes. The program flow goes like this:

  • lines 5 “ 11 (exception object created on line 11);

  • lines 18 “ 22 (catch block);

  • lines 23 “ 25 (finally block).

Third case: enter a proper integer
 c:\expt>test Enter a number:  70  running finally block 

No exceptions were thrown, the two catch blocks were skipped , and the finally block executes. The program flow goes like this:

  • lines 5 “ 12;

  • lines 23 “ 25 (finally block).

13.1.2 Exception example 2: nested try

Like Java, you can have multiple levels of try-catch nesting. Instead of a fully fledged code example, I will step through a skeletal code fragment, and discuss the program flow when an exception occurs at different points in the code.

I am using an example of one try-catch block immediately enclosed within another. Though you usually do not write nested try blocks within a single method, this exercise will clarify any conceptual uncertainties.

 10: try{ 11:       // statement 1 12:       // statement 2 13:  try{   14:         // statement 3   15:         // statement 4   16:       }   17:       catch(Exception1 e){   18:         // statement 5   19:         // statement 6   20:       }   21:       catch(Exception2 e){   22:         // statement 7   23:         // statement 8   24:       }   25:       finally{   26:         // statement 9   27:         // statement 10   28:       }  29:       // statement 11 30:       // statement 12 31:  } 32:  catch (Exception1 e){ 33:    // statement 13 34:    // statement 14 35:  } 36:  catch (Exception2 e){ 37:    // statement 15 38:    // statement 16 39:  } 40:  catch (Exception3 e){ 41:    // statement 17 42:    // statement 18 43:  } 44:  finally{ 45:    // statement 19 46:    // statement 20 47:  } 
First case: Exception2 thrown on statement 3 only

Figure 13.1 shows the program flow.

Figure 13.1. Program flow: Exception2 thrown on statement 3.

graphics/13fig01.gif

The exception is caught at line 21. The finally block completes and, since the exception has been handled properly in the inner try-catch block, the exception is not propagated to the next enclosing try block.

Second case: statement 3 throws Exception3 only

Figure 13.2 shows the program flow for this case.

Figure 13.2. Program flow: Exception3 thrown on statement 3.

graphics/13fig02.gif

Since the inner try-catch block cannot handle Exception3 , it is propagated to the next enclosing try block. Note that the inner finally block (lines 25 “ 28) executes first before control leaves the inner block. The exception is caught on line 40, and the enclosing finally block executes after the exception is handled.

Third case: statement 3 throws Exception1 and statement 5 throws Exception2

Figure 13.3 shows the program flow.

Figure 13.3. Two exceptions thrown.

graphics/13fig03.gif

This is a pretty interesting scenario. An exception is thrown within the inner catch block itself. (Some programmers do practice this deliberately when they want to propagate the exception 'outwards'. After catching an exception, and performing some initial handling, a more descriptive new exception is created and thrown out to be handled by a higher-level exception handler.)

At line 18 (statement 5), note that when Exception2 is thrown it will no longer be handled by the inner catch block on line 21. Any exception thrown in lines 17 “ 28 will be propagated to the next enclosing try block for processing. Nevertheless, the inner finally block (lines 25 “ 28) will be executed first before control goes out to the next enclosing try block.

13.1.3 Exception example 3: user-defined exception

Here is a more complex example showing how new user-defined exception objects are created and 'thrown back several levels'.

 1: using System;  2:  3: class NotEnuffMoneyException:Exception{  4:   public float Debt;  5:   public NotEnuffMoneyException(float debt){  6:     this.Debt = debt;  7:   }  8: }  9: 10: class TestClass{ 11:   public static void Main(){ 12: 13:     try{ 14:       CollectFood(); 15:     } 16:     catch (NotEnuffMoneyException e){ 17:       Console.WriteLine(e.Message); 18:       Console.WriteLine("Debt: $" + e.Debt); 19:     } 20:   } 21: 22:   static void CollectFood(){ 23:     PayForFood(); 24:   } 25: 26:   static void PayForFood(){ 27:     throw new NotEnuffMoneyException(2.5f); 28:   } 29: } 

Output:

 c:\expt>test Exception of type NotEnuffMoneyException was thrown. .5 

A new exception class called NotEnuffMoneyException [3] is created (on lines 3 “ 8). Notice that NotEnuffMoneyException is a subclass of System.Exception . In C#, all exception classes must be subclasses of System.Exception , directly or indirectly. Main() calls CollectFood() , which in turn calls BuyFood() . BuyFood() throws a new NotEnuffMoneyException to CollectFood() . CollectFood() hasn't got the necessary exception handling mechanism to handle this, and so it automatically throws it backwards (or upwards) to Main() , where it is handled. [4]

[3] Although not compulsory, it is customary to name your exception classes Something Exception . I would suggest that you stick to this convention, which is followed by most C# and Java communities.

[4] In my example here, NotEnuffMoneyException obviously wasn't well encapsulated. In a real program, I would have chosen to make Debt a private field, which is accessible via a public C# property (see Chapter 20) or accessor method. I didn't want to bring in properties and encapsulation to dilute the focus “ which is exception handling.

Hopefully, the three exception examples above have 'warmed you up' a bit. There haven't really been any distinctive differences between C# and Java yet. I shall now discuss some general similarities first, followed by the differences.

Like Java
  • You place code which may produce an exception into a try block, and can use one or more catch blocks to catch any exceptions.

  • The principle of exception shadowing still applies “ if you have multiple catch blocks, you must ensure that the catch blocks in front cannot catch exceptions which are superclasses of the exceptions caught in later catch blocks. For example, the code fragment which follows will not compile because System.FormatException is a subclass of System.Exception . You must catch System.FormatException before System.Exception .

     10: // will not compile 11: try{ 12:   int number1=System.Convert.ToInt32(userInput); 13: } 14: catch  (Exception e)  { 15:   // do something 16: } 17: catch  (FormatException e)  { 18:    // do something 19: } 
  • The finally block executes regardless of whether an exception happens or not.

  • Both the catch and the finally blocks are optional. However, you cannot have a try block without at least a catch or a finally block. You need not have any catch blocks, as long as there is a finally block. In this case, the finally block will execute before the exception object is propagated backwards/upwards.

     10: // perfectly legal  try without catch block 11: try{ 12:   // do something 13: } 14: finally{ 15:   // do something 16: } 
  • All exception classes are subclasses of one main exception class. In C#, this is System.Exception . If you want to write your own exception class, it must extend System.Exception or one of its subclasses. More information about the C# exception hierarchy will be given in the next section.

Unlike Java
  • In Java, exception classes are categorized into two types “ checked [5] and unchecked exceptions. In C#, no such differentiation exists. All C# exceptions are unchecked. [6]

    [5] Java's java.io.FileNotFoundException and java.io.IOException are the most famous examples of checked exceptions. Here is a refresher on Java's checked and unchecked exceptions “ checked exceptions are exceptions that must be handled or thrown out explicitly from the method (using the Java throws keyword). Unchecked exceptions are exceptions which do not need to be explicitly handled or thrown out of the method. Unchecked exceptions in Java are java.lang.RuntimeException and its subclasses. All other exceptions in Java are considered checked.

    [6] I have come across literature which states that all C# exceptions are runtime exceptions (which are unchecked exceptions). This is not wrong, but since C# does not categorize exceptions into checked or unchecked, the terms 'checked' and 'unchecked' do not make sense from a C# perspective.

  • Java insists that checked exceptions be handled or thrown. In C#, there is no need to trap for any type of exception at all. The following code opens temp.txt and prints to the console the contents of the file. [7] A System.IO.FileNotFoundException is thrown if the file is not found (on line 6), but there is no need to write codes for exception handling.

    [7] See Chapter 18 for more information on File I/O. Although file I/O will not be discussed here, the use of any I/O related class should be clear enough to be understood in this chapter.

     1: using System;  2: using System.IO;  3:  4: class TestClass{  5:   public static void Main(){  6:     StreamReader sr = File.OpenText("temp.txt");  7:     String input;  8:     while ((input=sr.ReadLine())!=null) {  9:       Console.WriteLine(input); 10:     } 11:  } 12: } 
  • It follows then that C# has no throws keyword. Java uses the throws keyword in the method declaration to pass checked exceptions to the calling method for processing. In C#, all exceptions are unchecked, and are hence automatically propagated if not handled locally.

  • It is permissible (and encouraged) not to assign the exception object with a variable name if it is not needed. The following code fragment actually causes a compiler warning because e is declared but never used:

     10: try{ 11:    // do something 12: } 13:  catch (Exception e){  14:   Console.WriteLine("an exception occurred"); 15: } 

Compiler warning:

 test.cs(13,1): warning CS0168: The variable 'e' is declared but never used. 

In such cases, you can replace line 13 with

 13: catch (Exception){ 

so that no unused local variable is declared. In contrast, Java insists that the local variable referring to the caught exception object be specified, regardless of whether it is used in the catch block.

  • Within a catch block, you can just use the throw keyword to re-throw the caught exception to the calling method. There is no need to specify the exception variable (as in throw e , where e is a reference to the caught exception object). This removes the need to declare the local variable referring to the exception object in the catch block if it is never used (as explained in the previous point).

     10: try{ 11:   // do something 12: } 13: catch (Exception){ 14:   // do some local processing here 15:  throw;  // throw to calling method the same exception 16: } 


From Java to C#. A Developers Guide
From Java to C#: A Developers Guide
ISBN: 0321136225
EAN: 2147483647
Year: 2003
Pages: 221
Authors: Heng Ngee Mok

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net