exception handlingc#

Upload: venkat-chittidi

Post on 05-Apr-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/31/2019 Exception Handlingc#

    1/17

    Exception Handling in C#

    Author: Wim UYTTERSPROT,[email protected]

    Introduction

    In C#, we have a built-in support for exception handling at our disposal, which is used

    for exceptions to the usual instruction route.

    The key to understand the concept of an exception and of the exception handling, is to

    not consider an exception as a fatal error, but as a deviant condition to which should be

    reacted.

    Exceptions should be handled in a try-catch block or a try-catch-finallyblock.Furthermore, an exception can be caused explicitly by means of the throwstatement.

    .NET Exceptions

    The exception handling mechanism is not only to be found in the C# language, but also

    in the .NET Common Language Runtime: each method of the .NET Framework is thus

    conceived that it throws an exception as soon as an error or an anomaly occurs.

    Consequently, being able to treat exceptions is an essential skill for each .NET

    programmer, no matter which language he/she uses.

    When an exception is thrown, an exception object is passed on to the call-stack* until an

    exception handler is found. If an exception handler is found, the program is further

    executed as from the exception handler that handled the exception. If no exception

    handler is found on the call stack, the .NET Common Language Runtime stops the

    process in which the exception has been thrown, and the runtime shows the name, the

    message string and stack trace of the exception. It is each .NET programmers

    responsibility to catch all exceptions so that his/her program doesnt suddenly crash.

    *The call stack is a list of functions that have been called. If, for example, the Main

    function calls the function Fa, which in its turn calls the function Fb, which calls Fc, then

    Main, Fa, Fb and Fc are to be found in that order on the call stack.

    The .NET exception mechanism is offered in both C# and VB.NET and largely corresponds

    to that of JAVA or that of C++. VB6 programmers only know error trapping, which is

    much less powerful.

    mailto:[email protected]:[email protected]:[email protected]:[email protected]
  • 7/31/2019 Exception Handlingc#

    2/17

    The try-catch block

    Lets start with a classic example: the integer division by zero (notice the backslash):

    int z = 0;int i = 1/z;

    When we execute this code, we get the following output, after which the execution of

    the code is abruptly stopped:

    Unhandled Exception: System.DivideByZeroException/Attempted to divide by zero. at ...

    If we want to intercept this exception in our code, we will have to make use ofException

    handling. For this purpose we use a try-catch block:

    try-catch block

    try{

    int z=0;int i = 1/z;

    }catch{

    Console.WriteLine("Exception handled: Division by zero");

    }

    As a result, the exception is handled and the process isnt stopped abruptly anymore;

    quite the contrary: the execution of the code continues after the catch statement.

    The Exception object

    The Exception base class

    If we want to get more information about an exception, we can define an exception

    object as argument in the catch statement. This exception object has to be of type

    System.Exception (as in the following example) or from a derived class:

    try{

  • 7/31/2019 Exception Handlingc#

    3/17

    int z = 0;int i = 1/z;

    }catch (Exception e){

    Console.WriteLine(e.Message);}

    The Message property of the Exception class can be questioned to get to know the

    reason of the exception. The example above contains e.Message:

    Attempted to divide by zero.

    For the Exception object, we can also make use of a derivative of an Exception class, e.g.

    the System.DivideByZeroException:

    try{

    int z = 0;int i = 1/z;

    }catch (DivideByZeroException e){

    Console.WriteLine(e.Message);}

    For your information: The DivideByZeroException class is derived from the

    ArithmeticException, which is in its turn derived from the SystemException, which is in its

    turn derived from Exception.

    Derived Exception classes

    Lets have a look at another classic example, namely the IndexOutOfRangeException:

    int[] a = new int[5];a[7] = 10;

    When we execute this code, we get the following output, after which the execution of

    the code stops abruptly:

    Unhandled Exception: System.IndexOutOfRangeException:An exception of type System.IndexOutOfRangeException was thrown. at ...

  • 7/31/2019 Exception Handlingc#

    4/17

    In order to catch this exception, we can immediately make use of a try-catch block with

    as argument an IndexOutOfRangeException object:

    int[] a = new int[5];

    try{

    a[7] = 10;}catch (IndexOutOfRangeException e){

    Console.WriteLine("Exception handled: " + e.Message);}

    Exceptions in functions

    Usually, the exception handling mechanism is used to catch exceptions that are causedin functions.

    When an exception is caused in a function, the runtime first looks for an exception

    handler in that function itself. If the runtime doesnt find one there, it looks for an

    exception handler in the code that has called the function. Then there are two

    possibilities:

    In the code calling the function, the runtime find or doesnt find an exception handler:

    There is an exception handler:Then the exception handler deals with the exception, whereupon the instruction,

    immediately following the try-catch block, is executed.

    There is no exception handler:The runtime then jumps to a higher function in the calling stack with the

    intention of finding an exception handler there. If the runtime cant find an

    exception handler anywhere in the calling stack, then the runtime itself handles

    the exception by abruptly stopping the execution of the program.

    First example

    Lets have a look at the Toons collection class in the following example:

    public class Toons{

    private Toon[] toons;

    public Toons(int n)

  • 7/31/2019 Exception Handlingc#

    5/17

  • 7/31/2019 Exception Handlingc#

    6/17

    {//...

    }

    If we want, on the other hand, to catch the exception in the GetAt function, then it can

    be defined, for example, as follows:

    public Toon GetAt(int index){

    try{

    return toons[index];}catch (IndexOutOfRangeException){

    return null;}

    }

    In our example, this solution is not preferable. Despite the fact that the

    IndexOutOfRangeException has been intercepted in GetAt(5), a new exception occurs,

    this time of type NullReferenceException. While catching the

    IndexOutOfRangeException in GetAt(5), Nothing is returned to serve as a reference to

    call the Draw() function with.

    Second example

    In the second example we try to convert a string variable into a decimal by means of theConvert.ToDecimal function. But this instruction can easily go wrong, for example, when

    the string is not well-formed and as a consequence not convertible to a decimal value. If

    the conversion is unsuccessful, the ToDecimal function will throw a FormatException

    object, as we can read in the C# documentation of the function ToDecimal. This

    exception should be caught on the spot where the ToDecimal is called:

    string s = "1B";decimal d;

    try

    {d = s.ToDecimal();

    }catch{}

  • 7/31/2019 Exception Handlingc#

    7/17

    Multiple exception handlers

    If we want to take several types of exceptions into account, then its best to make use of

    a multiple exception handler block, this is one block with several immediately successive

    catch statements belonging to one single try statement.

    We illustrate this in the following example in which 3 kinds of exceptions can be thrown.

    Notice that only one single try has been defined, with in addition 3 different catch

    statements with each time an argument of another type.

    string[] textTarget;// code before try block

    try{

    textTarget = new string[n];

    for (int i=0; i

  • 7/31/2019 Exception Handlingc#

    8/17

    In this way, the execution of the code passes off smoothly, and we get the following

    output:

    At end of try blockAfter try-catch block

    Second situation

    As second situation, we look at the case in which the reference textSource equals

    nothing, for instance:

    string[] textSource = null;int n = 3;

    This makes the execution of the code go wrong as soon as the first element is called.

    The caused NullReferenceException is caught by the first catch statement. We get thefollowing output:

    The value nothing was found where an instance of an object was required.:textSource is nothingAfter try-catch block

    Third situation

    Next, we test the situation in which n, for example, equals 4, so it is bigger than the

    amount of available elements in the array:

    string[] textSource = {"Welcome ", "to ", "ToonTown "};int n = 4;

    As a result, the execution of the code goes wrong as soon as the index 3 is used to call

    the 4th element in the textSource array. The caused IndexOutOfRangeException is

    caught by the second catch statement. And we get following output:

    An exception of type System.IndexOutOfRangeException was thrown.:textSource.Length is less than n

    After try-catch block

    Last situation

    The last one on the list is the situation in which n is, for example, negative, lets say -1:

    string[] textSource = {"Welcome ", "to ", "ToonTown "};int n = -1;

  • 7/31/2019 Exception Handlingc#

    9/17

    This makes the execution of the code go wrong in the allocation for textTarget. This is

    the output we get:

    An exception of type System.OutOfMemoryException was thrown.:n has negative valueAfter try-catch block

    Order of the catch statements

    When drawing up an exception handling block, you always have to keep in mind which

    order you put the catch statements in. For when an exception occurs, the catch

    statements are always called in that order.

    For exceptions between which an inheritance relation exists, this order is also checked

    by the C# compiler. The compiler, for instance, doesnt accept that we try to catch a

    more general exception before the derived exception. If this were permitted, the general

    exception catch would catch all exceptions.

    The following example shows that the order of the catch statements is important: as

    IndexOutOfRangeException is a subclass of Exception, the subclass catch statement (=

    the second catch) can never be reached here, which doesnt make sense.

    int[] a = new int[5];try

    { a[7] = 10;}catch (Exception e){

    Console.WriteLine("Exception handled: " + e.Message);}catch (IndexOutOfRangeException e) // compiler error{

    Console.WriteLine("Exception handled: " + e.Message);}

    The finally block

    When a program reserves resources, it is important that the reserved resources are

    always released after being used. This applies to both situations: when everything goes

    smoothly, and when something goes wrong and an exception is thrown. (Reserving

    resources implies for example: opening a file, opening a connection, starting a process,

  • 7/31/2019 Exception Handlingc#

    10/17

    using a switch,...) To solve this kind of problems easily, C# provides a powerful

    instrument: the finally-statement within a try-(catch)-finally block.

    Take the simple example of a switch being turned on. The success or otherwise of the

    operations that follow on the turning on of the switch doesnt matter: the switch needs

    to be turned off again at the end. In the following exemplary code, this is realized by

    putting the turning off of the switch in a finally-block.

    try{

    flag = true;Console.WriteLine("In try-block: flag is " + flag);Console.WriteLine("Do something wild");string wild = null;wild.Trim(); // throws exception. Comment this instruction.

    }finally

    {flag = false;Console.WriteLine("In finally-block: flag is " + flag);

    }Console.WriteLine("After try-finally block: flag is " + flag);

    In this block, the instruction wild.Trim() throws an exception of type

    NullReferenceException. This exception is not caught here so the execution of this

    example is abruptly stopped. However, the finally-block is still executed and the flag is

    reset in its original condition. This example gives the following output:

    In try-block: flag is TrueDo something wildException occurred: NullReferenceException: (...) at System.String.Trim()In finally-block: flag is False

    Mostly, a finally statement is combined with one or more catch statements. For example:

    try{

    flag = true;Console.WriteLine("In try-block: flag is " + flag);Console.WriteLine("Do something wild");string wild = null;

    wild.Trim(); // throws exception. Comment this instruction.}catch (Exception e)

    {

    Console.WriteLine("Exception catched");

    }

    finally{

    flag = false;Console.WriteLine("In finally-block: flag is " + flag);

  • 7/31/2019 Exception Handlingc#

    11/17

    }Console.WriteLine("After try-finally block: flag is " + flag);

    Now the exception is dealt with first, and then the finally-block is executed. This time,

    the output is:

    In try-block: flag is Truedo something wildException catched.

    In finally-block: flag is FalseAfter try-finally block: flag is False

    Remark: the order try-catch-(catch)-finally is obligatory. In C#, it is not possible to place

    the finally-block in front of the catch-block.

    Throwing exceptions

    The exception mechanism allows us to cause our exceptions ourselves by using the

    throw statement.

    In .NET, only objects of type Exception or of derived types can be thrown.

    In JAVA, exceptions also have to be of type Exception or derived type; moreover, in JAVA

    there is also the Throwable class, which is the base class of the Exception and the Error

    class. In oplocation to VB.NET, C# and so also to JAVA, in C++ an exception can be of any

    type.

    Simple example

    Lets have a look at the following example. If the input string is too long, an Exception

    object will be instantiated via the Exception constructor which we provide with the

    message of the exception as argument. Next, this exception object is thrown by means

    of the throw statement.

    string s = Console.In.ReadLine();if (s.Length > 20){

    throw new Exception("Input string is too long");}

  • 7/31/2019 Exception Handlingc#

    12/17

    Test this code by means of a string that contains more than 20 characters. The output is

    then for example:

    Welcome to the United ToonTownsUnhandled Exception: System.Exception: input string is too long at (...)

    It is obvious that throwing exceptions is also possible via a reference of type Exception:

    Exception e;e = new Exception("input string is too long");throw e;

    We repeat again that an Exception object needs to be thrown, with the emphasis on

    object. Although the compiler accepts the following code, in runtime we cause a

    NullReferenceException because a reference equal to nothing is thrown:

    Exception e = null;throw e;

    Creating your own Exception class

    In .NET and consequently in C#, an exception has to be an object of type Exception or

    derived from it. In other words, this means that we can define our own exception classes

    by deriving them from Exception.

    In the example of our Toon class, we have, for instance, the Zoom function in which we

    only let the scale of a Toon object adopt a value within the interval of 0 up to 500%. We

    now want to adjust the Zoom function in such a way that it throws an exception each

    time the value exceeds the interval.

    Subclassing the Exception-class

    For that purpose we first define an InvalidScaleException class:

    public class InvalideScaleException : Exception{

    private double invalidscale;

    public InvalideScaleException(double invalidscale){

    this.invalidscale = invalidscale;}

  • 7/31/2019 Exception Handlingc#

    13/17

    public override string ToString(){

    return "Invalid scale with value " + invalidscale;}

    }

    We can simplify the definition of this class if we dont want to make use of the

    invalidscale member:

    public class InvalideScaleException : Exception{

    public InvalideScaleException(double invalidscale): base("Invalid scale with value " + invalidscale)

    {}}

    Notice that the invalidscale parameter in string format in the definition above is passedon to the constructor of the base class (this is Exception).

    Now, we throw an InvalideScaleException object in the Zoom function when the scale

    value goes beyond the accepted values:

    public void Zoom (double ratio){

    double oldScale = Scale;Scale *= ratio;if (oldScale != Scale){

    Draw();}else{

    throw new InvalideScaleException(scale*ratio);}

    }

    This InvalideScaleException object is caught on the place where we call the Zoom

    function:

    Toon fred = new Toon( null, "Fred Flintstone", "fred.wmf", 100, 200, 1.0);try{

    fred.Scale = .75;fred.Zoom(7.0); // throws InvalideScaleExceptionfred.Move(102, 202);

    }catch (InvalideScaleException e){

    Console.WriteLine(e.Message);

  • 7/31/2019 Exception Handlingc#

    14/17

    }

    This logically results in:

    Invalid scale with value 5.25

    We go one step further: in case of an incorrect value, we now throw the

    InvalideScaleException object from the setter of the Scale property of the Toon class

    derived from the Shape class:

    public class Toon : Shape {...

    public double Scale{

    override set{

    if (value>0.0 && value

  • 7/31/2019 Exception Handlingc#

    15/17

    public class Shape : IScalable{...

    public virtual double Scale{

    get{

    return scale;}set{

    scale = value;}

    }

    public double scale;

    public void Zoom (double ratio){

    Scale *= ratio; // override in Toon class can throw an exception

    Draw();}

    }

    We verify again what happens when we call the Zoom function (as we did above):

    fred.Scale = .75fred.Zoom(7.0) throws InvalideScaleException

    The Zoom function of the Shape class then calls the Scale setter of the Toon class

    (because the Scale setter of the Shape class is overridden in the Toon class). In the Scale

    setter, the Exception object is trown:

    Unhandled Exception occurred: ToonTown.InvalideScaleException:Invalid scale with value 5.25

    at ToonTown.Toon.set_Scale(ByVal value As Double) in (...)at ToonTown.Shape.Zoom(ByVal ratio As Double) in (...)at ToonTown.CarToon.Main() in (...)

  • 7/31/2019 Exception Handlingc#

    16/17

    Hierarchy of the own Exception classes

    It is often useful to structure ones own exceptions hierarchically. Look for instance at

    the following example: the ShapeException has been defined as the base class for the

    derived InvalidScaleException and the InvalidLocationException classes.

    public class ShapeException : Exception{

    public ShapeException(string message): base(message){}

    }

    public class InvalideScaleException : ShapeException{

    public InvalideScaleException(double invalidscale): base("Invalid scale with value " + invalidscale)

    {}

    }

    public class InvalideLocationException : ShapeException{

    public InvalideLocationException(Point invalidpoint): base("Invalid location with value " + invalidpoint){}

    }

    A hierarchy of own exception classes allows us to distinguish in great detail between thedifferent kinds of exceptions (of derived type), by means of a multiple exception

    handlers block:

    Toon fred;try{

    // ...}catch (InvalideScaleException e){

    // ...

    }catch (InvalideLocationException e){

    // ...}

    If there is no need for a detailed distinction, a simple catch statement can be used to

    catch exceptions of the base type, and consequently also the exceptions of the derived

  • 7/31/2019 Exception Handlingc#

    17/17

    types. In the following example, not only the exception objects of types ShapeException

    are caught, but also the types derived from it, namely InvalideScaleException and

    InvalideLocationException, and this with only one single catch statement:

    Toon fred;

    try{

    fred = new Toon( null, "Fred Flintstone", "fred.wmf", 100, 200, 1.0);fred.Scale = 7.5fred.Move(-1, 100);

    }catch (ShapeException e){

    // catches also InvalideScaleException and InvalideLocationExceptionConsole.WriteLine(e.Message);

    }