Exception Bubbling in C#


Overview

Exceptions are used to indicate that an error has occurred while running the program. Exception objects that describe an error are created and then thrown with the throw keyword. The runtime then searches for the most compatible exception handler.

In C# language's exception handling features helps to deal with any unexpected or exceptional situations that occur when a program is running. Exception handling uses the try, catch, and finally keywords to

  • Try actions that may not succeed
  • To handle failures when decided that it is reasonable to do so
  • To clean up resources afterwards

and Exceptions can be generated

  • By the common language runtime (CLR)
  • By the .NET Framework or any third-party libraries
  • Or by application code

Exceptions are created by using the throw keyword.

↑ Return to Top


Syntax

An error can occur at almost any statement. Checking for all these errors becomes unbearably complex. Exception handling separates this logic. It simplifies control flow. C# exception handling is built upon four keywords: try, catch, finally, and throw.

The try block encloses the statements that might throw an exception whereas catch handles an exception if one exists. The finally can be used for doing any clean up process. The general form of try-catch-finally in C# is shown below.

try
{
   // Statement which can cause an exception.
}
catch(TypeOfException ex)
{
   // Statements for handling the exception
}
finally
{
   //Any cleanup code
}

↑ Return to Top


Uncaught Exception

The following program will compile but will show an error during execution. The division by zero is a runtime anomaly and the program terminates with an error message. Any uncaught exceptions in the current context propagate to a higher context and looks for an appropriate catch block to handle it. If it can't find any suitable catch blocks then the default mechanism of the .NET runtime will terminate the execution of the entire program.

Code

using System;
 
namespace UncaughtException
{
    class Program
    {
        static void  Main(string[] args)
        {
            int x = 0;
            int div = 100 / x;
            Console.WriteLine(div);
            Console.ReadLine();
        }
    }
}

Output

↑ Return to Top


Handling Exception

In C# it provides a structured solution for the exception handling in the form of try and catch blocks. Using these blocks the core program statements are separated from the error-handling statements. As we see from the above example it can't find any suitable catch blocks then the default mechanism of the .NET runtime will terminate the execution of the entire program. To prevent this from happening we modified the above program with error handling blocks. These are implemented with try, catch keywords. So, the modified form with the exception handling mechanism is as follows.

Code

using System;
 
namespace ExceptionHandling
{
    class Program
    {
        static void  Main(string[] args)
        {
            try
            {
                int x = 0;
                int div = 100 / x;
                Console.WriteLine(div);
                Console.ReadLine();
            }
            catch(Exception ex)
            {
                Console.WriteLine("Error message: " + ex.ToString());
                Console.ReadLine();
            }
        }
    }
     
}

Output

↑ Return to Top


Throwing Exception

It is possible to throw an exception programmatically. throw is basically like throwing an exception from that point, so the stack trace would only go to where we are issuing the "throw". This throwing exception is handled by catch block. The throw statement is used for this purpose. The general form of throwing an exception is as follows.

Code

using System;
 
namespace ExceptionThrowing
{
    class Program
    {
        static void  Main(string[] args)
        {
            try
            {
                throw new  Exception();
            }
            catch(Exception ex)
            {
                Console.WriteLine("Error message: " + ex.ToString());
                Console.ReadLine();
            }
        }
    }
     
}

Output

↑ Return to Top


Exception Bubbling

In the above examples, we saw that Exception is handled in the catch block. Where main() method runs the code and raised exception is handled in the catch block. Imagine the situation what happens if there are multiple nested function calls, and exception occurred in the fourth or fifth nested call.

  • Function1(): Calls Function2 within the try block and handles the exception in catch block
  • Function2(): Makes a call to the function Function3. But it neither wraps the call for Function3 in the try block nor has the exception handler
  • Function3(): Makes a call to the function Function4. But it neither wraps the call for Function4 in the try block nor has the exception handler
  • Function4(): Makes a call to the function Function5. But it neither wraps the call for Function5 in the try block nor has the exception handler
  • Function5(): Raises an Exception

Note, when the exception is thrown by the function Function5, even though the caller is Function2, as there is no catch handler, the execution comes out of Function2 and enters the catch block of Function1. Travelling back from

          Function5 -> Function4 -> Function3 -> Function2 -> Function1

is known as Stack Unwinding. And exception occurred in Function5 is handled in Function1 even when there is no handler at Function2 is known as Exception Bubbling.

So, when we define a try/catch block, the handler defined in the catch block will catch exceptions that originate directly from the code in the try block. The handler will also catch exceptions that originate in methods called from the code in the try block, or from code that those methods call. In short, a catch block will catch any exceptions thrown by code that executes as a result of executing the block of code in the try block. (Assuming that the exception is not caught elsewhere).

Below is the example that demonstrates the Exception Bubbling.

Code

using System;
 
namespace ExceptionBubbling
{
    class Program
    {
        static void  Main(string[] args)
        {
            try
            {
                Console.WriteLine("Making Call to Function1()");
                Function1();
                Console.WriteLine("Successfully returned from Function1()");
            }
            catch (Exception ex)
            {
                Console.WriteLine("\nFollowing exception occured:\n\n" + ex);
            }
            finally
            {
                Console.WriteLine("\n\nInside finally block.");
                Console.ReadLine();
            }
        }
 
        public static  void Function1()
        {
            Console.WriteLine("Inside Function1 -> Making Call to Function2()");
            Function2();
            Console.WriteLine("Successfully returned from Function2()");
        }
        public static  void Function2()
        {
            Console.WriteLine("Inside Function2 -> Making Call to Function3()");
            Function3();
            Console.WriteLine("Successfully returned from Function3()");
        }
        public static  void Function3()
        {
            Console.WriteLine("Inside Function3 -> Making Call to Function4()");
            Function4();
            Console.WriteLine("Successfully returned from Function4()");
        }
        public static  void Function4()
        {
            Console.WriteLine("Inside Function4 -> Making Call to Function5()");
            Function5();
            Console.WriteLine("Successfully returned from Function5()");
        }
        public static  void Function5()
        {
            Console.WriteLine("Inside Function5");
            throw new  Exception();            
        }
    }
}

Output

Explanation

As we can see from the above output exception occurred in Function5 is passed to its calling function Function4 because there is no catch block to handle it and this continues till it reaches to Main() method as catch block or exception handling mechanism is only implemented here. And it enters the finally block of Main() method

While finding the catch block even though other function didn't through the exception the stack is getting filled with them in backward. For this while stack should filled with one exception it is getting filled and bubbling up with five exceptions.

Now let's say we had a try/ catch block handled in Function5 then what would happen? As Function5 is wrap with try/ catch block the exception will be handled there and code will return successfully.

Below is the example that demonstrates the Exception Handling:

Modified Code

using System;
 
namespace ExceptionBubbling
{
    class Program
    {
        static void  Main(string[] args)
        {
            try
            {
                Console.WriteLine("Making Call to Function1()");
                Function1();
                Console.WriteLine("Successfully returned from Function1()");
            }
            catch (Exception ex)
            {
                Console.WriteLine("\nFollowing exception occured:\n\n" + ex);
            }
            finally
            {
                Console.WriteLine("\n\nInside finally block.");
                Console.ReadLine();
            }
        }
 
        public static  void Function1()
        {
            Console.WriteLine("Inside Function1 -> Making Call to Function2()");
            Function2();
            Console.WriteLine("Successfully returned from Function2()");
        }
        public static  void Function2()
        {
            Console.WriteLine("Inside Function2 -> Making Call to Function3()");
            Function3();
            Console.WriteLine("Successfully returned from Function3()");
        }
        public static  void Function3()
        {
            Console.WriteLine("Inside Function3 -> Making Call to Function4()");
            Function4();
            Console.WriteLine("Successfully returned from Function4()");
        }
        public static  void Function4()
        {
            Console.WriteLine("Inside Function4 -> Making Call to Function5()");
            Function5();
            Console.WriteLine("Successfully returned from Function5()");
        }
        public static  void Function5()
        {
            /*Console.WriteLine("Inside Function5");
            throw new Exception();*/
 
            //Exception handled
            try
            {
                Console.WriteLine("Inside Function5");
                throw new  Exception();
            }
            catch (Exception ex)
            {
                Console.WriteLine("\nFollowing exception occured:\n\n" + ex);
            }
            finally
            {
                Console.WriteLine("\n\nInside finally block of Function5().");
                Console.ReadLine();
            }
        }
    }
}

Output

If we comment out the finally block in Function5() code will goto to the finally block in the main() method in backward.

Explanation

Here we can see that there is an exception handling mechanism implemented in the Function5 so code is not going backward to find catch block and filling the stack. As exception is handled there code is then going backward to finally block in main() and returning successfully from each function.

↑ Return to Top


Summary

By now, we should have good understanding of what is an exception bubbling. An Exception will be caught in the inner catch block if appropriate filter found, otherwise will be caught by outer catch block. We can implement try/ catch block to prevent exception to bubbling up. Also we know how to clean up resources by implementing a finally block whose code is always executed before leaving a method.

↑ Return to Top


Reference

  • Exception Handling Fundamentals here
  • Best Practices for Exceptions here
  • Exception Handling Statements here
  • Handling and Throwing Exceptions here
  • Managing Exceptions with the Debugger here

↑ Return to Top


See Also

↑ Return to Top


Download

Download the Source Code used in the example from this link Download Source Code

↑ Return to Top


https://c.statcounter.com/11255324/0/d9043b45/0/