Exercise index of this lecture   Alphabetic index   Course home   

Exercises and solutions
Exception Handling


9.1   Exceptions in Convert.ToDouble  

The static methods in the static class System.Convert are able to convert values of one type to values of another type.

Consult the documentation of System.Convert.ToDouble. There are several overloads of this method. Which exceptions can occur by converting a string to a double?

Write a program which triggers these exceptions.

Finally, supply handlers of the exceptions. The handlers should report the problem on standard output, rethrow the exception, and then continue.

Solution

If you consult the documentation of the method ToDouble in class System.Format you will find out that ToDouble may throw a FormatException or an OverflowException.

The following program demonstrates the throwing and pseudo-handling of the exceptions FormatExceptions and OverflowException, as triggered by using System.Format.ToDouble on strings.

using System;

class Program{

  public static void Main(){
 
     double d = 1,
            e = 2,
            f = 3;

     try{
       e = Convert.ToDouble("123.45-6");
     }
     catch (FormatException){
        Console.WriteLine("We got a FormatException");
        throw;                                        // rethrowing
     }
     catch (OverflowException){
        Console.WriteLine("We got an OverflowException");
        throw;                                        // rethrowing
     }
     catch (Exception){
        Console.WriteLine("We got another Exception");
        throw;                                        // rethrowing
     }

     try{
       f = Convert.ToDouble("12355555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555");
       }
     catch (FormatException){
        Console.WriteLine("We got a FormatException");
        throw;                                        // rethrowing
     }
     catch (OverflowException){
        Console.WriteLine("We got an OverflowException");
        throw;                                        // rethrowing
     }
     catch (Exception){
        Console.WriteLine("We got another Exception");
        throw;                                        // rethrowing
     }

     Console.WriteLine("{0} {1} {2}", d, e, f);

  }

}

In order to provoke an OverflowException you must type very many ciffers before the decimal point (more than 300). You can also provide a string such as "1,8E308". Recall that the the minimum and maximum doubles are -1,79769313486232E+308 and 1,79769313486232E+308 respectively.

Due to the rethrowing, the output of the program only reveals the FormatException. The reason is that FormatException will lead to a program halt, and therefore we never come to the conversion of the long string with lots of fives. If you - against all recommendations - eliminate the rethrowing you can observe the OverflowException as well.


9.2   Exceptions in class Stack  

In the lecture about inheritance we specialized the abstract class Stack.

Now introduce exception handling in your non-abstract specialization of Stack. I suggest that you refine your own solution to the previous exercise. It is also possible to refine my solution.

More specifically, introduce one or more stack-related exception classes. The slide page "Raising and throwing exceptions in C#" tells you how to do it. Make sure to specialize the appropriate pre-existing exception class!

Arrange that Push on a full stack and that Pop/Top on an empty stack throw one of the new exceptions. Also, in the abstract stack class, make sure that ToggleTop throws an exception if the stack is empty, or if the stack only contains a single element.

Finally, in a sample client program such as this one, handle the exceptions that are thrown. In this exercises it is sufficient to report the errors on standard output.

Solution

We introduce a StackUnderflowException and a StackOverflowException together with the abstract class. Notice that the method ToggleTop throws the StackUnderflowException if the stack has less than two elements.

using System;

public abstract class Stack{ 
  
  abstract public void Push(Object el);

  abstract public void Pop();

  abstract public Object Top{
    get;}

  abstract public bool Full{
    get;}

  abstract public bool Empty{
    get;}

  abstract public int Size{
    get;}   

  public void ToggleTop(){
    if (Size >= 2){
      Object topEl1 = Top;  Pop();
      Object topEl2 = Top;  Pop();
      Push(topEl1); Push(topEl2);
    }
    else throw new StackUnderflowException();
  }   

  public override String ToString(){
    return("Stack");
  }
}

public class StackOverflowException: ApplicationException{
}

public class StackUnderflowException: ApplicationException{
}

Here follows the refined non-abstract specialization of class Stack, which includes exception handling:

using System;

public class AStack: Stack{ 

  private Object[] elements;
  private int nextPush;
  private int MaxSize; 

  public AStack(int MaxSize){
    elements = new Object[MaxSize];
    nextPush = 0;
    this.MaxSize = MaxSize;
  }
  
   public override void Push(Object el){
    if (!Full){
       elements[nextPush] = el;
       nextPush++;
     } else throw new StackOverflowException();
  }

   public override void Pop(){
     if (!Empty)
       nextPush--;
     else throw new StackUnderflowException();
   }

   public override Object Top{
    get{
      if (!Empty)
        return elements[nextPush-1];
      else throw new StackUnderflowException();
    }
   }

   public override bool Full{
    get{return nextPush >= MaxSize;}
   }

   public override bool Empty{
    get{return nextPush == 0;}
   }

   public override int Size{
    get{return nextPush;}
   }   

  public override String ToString(){
    string res = "Stack: ";
    for(int i = 0; i < nextPush; i++){
      res += elements[i] + " ";}
    return res;
  }

}

The following client triggers the requested exception handling:

using System;

class C{

  public static void Main(){

    Stack s = new AStack(10);

    try{
      Console.WriteLine("Empty: {0}", s.Empty);
      Console.WriteLine("Full: {0}", s.Full);
  
      s.Push(5); s.Push(6);  s.Push(7);      
      s.Push(15); s.Push(16);  s.Push(17); 
      s.Push(15); s.Push(16);  s.Push(17); 
      s.Push(15); 
  
      Console.WriteLine("{0}", s.Size);
      Console.WriteLine("{0}", s.Top);
      Console.WriteLine("{0}", s);
  
      s.Pop();s.Pop();s.Pop();s.Pop();s.Pop();s.Pop();s.Pop();s.Pop(); s.Pop(); s.Pop();
  
      Console.WriteLine("Empty: {0}", s.Empty);
      Console.WriteLine("Full: {0}", s.Full);
  
      Console.WriteLine("{0}", s);
      s.ToggleTop();
      Console.WriteLine("{0}", s);
   }
   catch (StackOverflowException){
     Console.WriteLine("StackOverflow");
   }
   catch (StackUnderflowException){
     Console.WriteLine("StackUnderflow");
   }

  }

}

Notice, in this client class, that the first encountered stack error causes termination of Main. There is no notion of 'returning back to' or 'retrying' the offending stack operation. This may not always be satisfactory.


9.3   More exceptions in class Stack  

In continuation of the previous exercise, we now wish to introduce the following somewhat unconventional handling of stack exceptions:

  • If you push an element on a full stack throw half of the elements away, and carry out the pushing.
  • If you pop/top an empty stack, push three dummy elements on the stack, and do the pop/top operation after this.

With these ideas, most stack programs will be able to terminate normally (run to the end).

I suggest that you introduce yet another specialization of the stack class, which specializes Push, Pop, and Top. The specialized stack operations should handle relevant stack-related exceptions, and delegate the real work to its superclass. Thus, in the specialized stack class, each stack operation, such as Push, you should embed base.push(el) in a try-catch control structure, which repairs the stack - as suggested above - in the catch clause.

Solution

Here is the additional stack specialization called HStack (for Handling Stack), in which exception handling occurs in the immediate neighborhood of each Push, Pop, and Top operation:

using System;

public class HStack: AStack{ 

   public HStack(int MaxSize): base(MaxSize){
   }
  
   public override void Push(Object el){
   try{
     base.Push(el);
   }
   catch (StackOverflowException){
     nextPush = nextPush/2;  // Effectively throwing half of the stack away!
     base.Push(el);
     Console.WriteLine("Removing half of the stack: {0}", this);
   }
  }

   public override void Pop(){
     try{
       base.Pop();
     }
     catch (StackUnderflowException){
       base.Push("El 1");  base.Push("El 2"); base.Push("El 3");
       base.Pop();
       Console.WriteLine("Adding 3 extra elements: {0}", this);
     }
  }

   public override Object Top{
     get{
       try{
         return base.Top;
       }
       catch (StackUnderflowException){
         base.Push("El 1");  base.Push("El 2"); base.Push("El 3");
         return base.Top;
       }
     }
   }

   // Empty, Full, Size and ToString are inherited

}

The class AStack (standing for ArrayStack), which is the base-class of HStack, corresponds to the solution of the previous exercise. We assume that instance variables of AStack are protected.

Notice that the version of HStack above discards the top part of the stack in case of a StackOverflowException. This is probably not what we want in a real application. It is a good exercise to remove the other half part of the stack. For this purpose, consider a new Stack operation which pops the stack in the opposite end of pushing. With such a stack, we approach a queue datatype.

Here is a sample client program, which in itself "handles" stack exceptions that remain unhandled:

using System;

class C{

  public static void Main(){

    Stack s = new HStack(5);

    try{
      Console.WriteLine("Empty: {0}", s.Empty);
      Console.WriteLine("Full: {0}", s.Full);
  
      s.Push(5); s.Push(6);  s.Push(7);      
      s.Push(15); s.Push(16);  s.Push(17); 
      s.Push(18); s.Push(19);  s.Push(20); 
      s.Push(21); 
  
      Console.WriteLine("{0}", s.Size);
      Console.WriteLine("{0}", s.Top);
      Console.WriteLine("{0}", s);
  
      s.Pop();s.Pop();s.Pop();s.Pop();s.Pop();s.Pop();s.Pop();s.Pop(); s.Pop(); s.Pop();
  
      Console.WriteLine("Empty: {0}", s.Empty);
      Console.WriteLine("Full: {0}", s.Full);
  
      Console.WriteLine("{0}", s);
      s.ToggleTop();
      Console.WriteLine("{0}", s);
   }
   catch (StackOverflowException){
     Console.WriteLine("StackOverflow");
   }
   catch (StackUnderflowException){
     Console.WriteLine("StackUnderflow");
   }

  }

}

The output of the program is as follows:

Empty: True
Full: False
Removing half of the stack: Stack: 5 6 17 
Removing half of the stack: Stack: 5 6 20 
4
21
Stack: 5 6 20 21 
Adding 3 extra elements: Stack: El 1 El 2 
Adding 3 extra elements: Stack: El 1 El 2 
Empty: True
Full: False
Stack: 
StackUnderflow


9.4   Revealing the propagation of exceptions  

We have written a program that reveals how exceptions are propagated. In the program output, we see that the calling chain is Main, M, N, P.

The program output does not, however, reveal that the chain is followed in reverse order in an attempt to find an appropriate exception handler.

Revise the program with handlers in M, N, and P that touch the exception without actually handling it. The handlers should reveal, on standard output, that P, N, and M are passed in an attempt to locate a relevant exception handler. Rethrow the exception in each case. See here how this can be done.

Solution

Here is my solution:

using System;

class ExceptionDemo{

  public static void Main(){
    int[] table = new int[6]{10,11,12,13,14,15};
    int idx = 6;

    Console.WriteLine("Main");
    try{
      M(table, idx);
    }   
    catch (IndexOutOfRangeException){
      M(table, AdjustIndex(idx,0,5));
    }
  }

  public static void M(int[] table, int idx){
    Console.WriteLine("Calling M(table,{0})", idx);
    try{
      N(table,idx);}
    catch {
      Console.WriteLine("Reversing through M");
      throw;
    }
  }

  public static void N(int[] table, int idx){
    Console.WriteLine("Calling N(table,{0})", idx);
    try {
      P(table,idx);}
    catch {
      Console.WriteLine("Reversing through N");
      throw;
    }
  }

  public static void P(int[] table, int idx){
    Console.WriteLine("Calling P(table,{0})", idx);
    try {
      Console.WriteLine("Accessing element {0}: {1}", 
                         idx, table[idx]);
    }
    catch {
      Console.WriteLine("Error in P. Reversing through P");
      throw;
    }
  }


  public static int AdjustIndex(int i, int low, int high){
    int res;
    if (i < low)
      res = low;
    else if (i > high)
      res = high;
    else res = i;

    return res;
  }
}

Notice the use of catch without specifying the type or name of the exception.

The output of the program is here:

Main
Calling M(table,6)
Calling N(table,6)
Calling P(table,6)
Error in P. Reversing through P
Reversing through N
Reversing through M
Calling M(table,5)
Calling N(table,5)
Calling P(table,5)
Accessing element 5: 15


Generated: Monday February 7, 2011, 12:19:03