Exception Management - Expert C# 5.0: with .NET 4.5 Framework (2013)

Expert C# 5.0: with .NET 4.5 Framework (2013)

CHAPTER 13

images

Exception Management

This chapter will discuss the exception and exception handling in .NET using C#. The chapter begins with the definition of exception and then explains how the CLR manages the exception in .NET by discussing exception-handling information in the Method Description table and how the CLR uses this exception-handling information to manage the exception. I will then discuss exception handling in .NET by examining the protected block and throw and rethrow statements in .NET. The chapter then explores the stack overwrite by discussing how the CLR manages the exception throughout the method call chain using the throw statement in addition to exception matching.

What Is an Exception?

A program consists of a sequence of instructions that are used to execute a specific operation based on the given data (if any) to produce an expected outcome of the operation. If in the execution time the instruction cannot do its operation based on the provided data, it will raise either an error or an exception for that operation to let the user know about this unexpected behavior. There are many possible situations for the system to raise an exception for an operation. In a system, there should be a proper mechanism to handle this unexpected behavior. Before we go further, let’s examine the example in Listing 13-1 to get a better understanding of the concept of the exception in the .NET application.

Listing 13-1. An Example of Division Operation

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
int a = 10, b = 5;
Division div = new Division();
Console.WriteLine("{0}/{1}={2}", a, b, div.Divide(a, b));
}

public class Division
{
public int Divide(int a, int b)
{
return a / b;
}
}
}
}

The program in Listing 13-1 is doing a divide operation based on the data passed via parameter a, b. For example, if a = 10 and b = 5, then it will return 2 as a result of the operation, and so on. Let’s test Listing 13-1 with the set of values given in Table 13-1.

images

This program will work normally with the values (10, 5) and (100, 50). When this program uses (100, 0) as input to do the divide operation, it will not work because anything divided by zero returns infinity. For this reason, in runtime the CLR will not be able to handle infinity as a result, so it will raise a notification with the reason for this unexpected behavior. This behavior is called exceptional behavior for a type, and in the point of view of .NET, it is an exception. For example, while you run the division program in Listing 13-1 with the value (100, 0) as input, the CLR will produce an error notification to describe the unexpected behavior of the division program, as demonstrated in Figure 13-1.

images

Figure 13-1. Example of the exception that occurred in the division program given in Listing 13-1

Therefore, the exception types of handled or unhandled are key concepts for defining the exceptional or unexpected behavior for a program in .NET, as demonstrated in Figure 13-1. When the CLR executes the division program from Listing 13-1 with the values 100 and 0, it cannot process the operation, and it shows the detailed information about this unexpected behavior of the program to let the user know about the unexpected behavior of the program.

In .NET, you use an exception-handling mechanism to manage unexpected behavior of the application. This mechanism is based on a simple rule, for example, mark the relevant code as a protected block and define some handlers associated with the protected block, which will be activated when any unexpected behavior is raised from the protected block (i.e., the handler block will deal with any unexpected behavior for the application). The protected block can be declared using the try statement and the handler blocks by using the catch and finally statements.

When an instance of the exception is instantiated, it contains the information regarding the exception. In runtime, the CLR transfers the program control from the point where the exception was raised to the appropriate handler block of the program that is the best match for the type of exception object that was instantiated. The CLR executes the matched handler block. If the CLR cannot find any matching handler block in the method, the program control moves back to the caller of the method to find the relevant handler block. It will keep continuing the search all the way up to the top of the method chain. If it does not find a handler block, the CLR will log the exception to the Windows log(which can be seen by the event viewer). It will leave the program control to the Windows error report, which will also write it to the Windows log.

Exception and .NET Framework

In .NET, the Exception class is defined in the System namespace of the mscorlib.dll assembly, as demonstrated in Figure 13-2.

images

Figure 13-2. System.Exception class in the mscorlib.dll assembly

The Exception class has the following definition:

public class Exception : ISerializable, _Exception
{
public Exception() {}
public Exception(string message) {}
protected Exception(SerializationInfo info, StreamingContext context) {}
public Exception(string message, Exception innerException) {}
public virtual Exception GetBaseException() {}
public virtual void
GetObjectData(SerializationInfo info, StreamingContext context) {}
public Type GetType() {}
public override string ToString() {}
public virtual IDictionary Data {}
public virtual string HelpLink {}
public Exception InnerException {}
public virtual string Message {}
public virtual string Source {}
public virtual string StackTrace {}
public MethodBase TargetSite {}
}

The System.Exception is a base class for all other Exception classes, such as DivisionByZeroException or NullReferenceException. The definition of the Exception class shows that it is implemented through the ISerializable and _Exception interfaces (defined in the System.Runtime.InteropService namespace of themscorlib.dll) and inherited from the System.Object (as are all classes in .NET). The _Exception interface contains most of the important properties for the Exception class, and this is discussed in the next section.

_Exception Interface

Let’s examine the _Exception interface, as shown in Listing 13-2, extracted via ildasm.exe from the mscorlib.dll.

Listing 13-2. Definition of the _Exception Interface

.class public interface abstract auto ansi _Exception
{
/* Code removed */
/* Defined property in the _Exception interface*/
.property instance string HelpLink {}
.property instance class System.Exception InnerException {}
.property instance string Message {}
.property instance string Source {}
.property instance string StackTrace {}
.property instance class System.Reflection.MethodBase TargetSite {}
}

The System.Exception class has a few important properties that are used to manage the exception details when an exception is raised by the CLR. In Table 13-2, these properties show where the CLR stores these properties to manage the exception.

images

Table 13-2 demonstrates that _message, _stackTraceString, _remoteStackTraceString, _helpURL, _source, _innerException, _exceptionMethod, and _data are the internal fields of the Exception class that is used by the CLR to encapsulate all the information related to the exception when it is raised.

How C# Compiler Handles Exception in Compile Time

In the compile time of a C# program, the C# compiler creates a Method State Description table for each of the methods defined in the class of a program. In the Method State Description table, the compiler sets an empty entry or possibly an array block of the Exception Handling Information Block (depending on the try...catch block presence in the code), and each of the methods in the Method State Description table will be linked with the Method table of the class. Figure 13-3 demonstrates the association between the Method table, Method State Description table, and Exception Handling Information Block or the Exception Entry.

images

Figure 13-3. Exception object process by the C# compiler

Figure 13-3 demonstrates how the C# compiler includes exception-handling information (if the code block in the method has been guarded using try...catch) in the Method State Description table to the method of a class. In the Method State Description table, there will be an empty entry or an array of the exception entry. Each of these items (exception entry) contains information about the protected, guarded, or try block, associate filters, and its handlers, such as catch handler, filter handler, finally handler, or a fault handler.

In .NET, each of the methods of a type (class) could possibly have an exception entry (if there is a try...catch block in the code or nothing if there is no try...catch block defined in the code), which will contain the exception-handling information regarding that method. The protected or guardedblock is defined using the trystart and trylength properties, and the filter/handler block is defined using handlerstart and handlerlength. Table 13-3 presents the definitions of the handlers data structure and the different exception-handler regions in a program.

images

The next section will explore how the CLR handles an exception in runtime.

Exploring Exception Details in Runtime

Let’s do a bit of research to determine how the C# compiler generates and uses the Method State Description table to store the exception-handling information used in the program. You can use windbg.exe to explore the exception-handling information for the method used in the code given in Listing 13-3.

Listing 13-3. An Example to Demonstrate Method Description Table Information in a Class

using System;

namespace Ch13
{
class MainClass
{
static void Main(string[] args)
{
ExceptionInMethodStateDescription exInMSD =
new ExceptionInMethodStateDescription();
exInMSD.MethodOne();
exInMSD.MethodTwo();
exInMSD.MethodThree();
}
}

public class ExceptionInMethodStateDescription
{
public void MethodOne()
{
try {}
catch (IndexOutOfRangeException indexOutOfRangeException) {}
}
public void MethodTwo()
{
try {}
catch (ArgumentException argumentException) {}
}
public void MethodThree()
{
try {}
catch (Exception exception) {}
}
}
}

When the C# compiler compiles the code as given in Listing 13-3, the MainClass and ExceptionInMethodStateDescription classes will have a Method table. The Method table of the MainClass will hold the Method Description table information for the MainClass and the same for the ExceptionInMethodStateDescription class. The number of methods in the MethodDescription table depends on the number of methods defined in the class. Each of these methods from the Method Description table from the class can be used to determine the associated exception-handling information for the methods demonstrated in the code given in Listing 13-3.

Let’s debug the executable (produced from Listing 13-3) using windbg.exe to explore the exception-handling information defined by the C# compiler in the compile time.

The executable file (Ch13.exe) produced from Listing 13-3 can be used in the windbg.exe to determine the related information about the Method table and method description and to find out how exception-handling information is stored in the method description table for an executable program (for example, the Ch13.exe). You will find the following information when you run the executable program using windbg.exe.

1. You will extract the Method Table address of the MainClass and ExceptionInMethodStateDescription class from the executable.

2. You will extract the Method Description Table information for each of the methods defined in the type, using the Method Table address information extracted the Step 1.

3. Using the Method Description Address retrieved from Step 2, you will retrieve the exception-handling information (if there is any) associated with each of the methods defined in the classes used in Listing 13-3.

Let’s debug the executable using the windbg.exe program.

1. Step 1: Extract the MethodTable address for the MainClass and ExceptionInMethodStateDescription using the Name2EE command in the windbg.exe.

!Name2EE Ch13.exe Ch13.MainClass
!Name2EE Ch13.exe Ch13.ExceptionInMethodStateDescription

These commands will return the MethodTable address information for MainClass and ExceptionInMethodStateDescription class shown here (output might vary when you run this locally):

Module: 00142e9c
Assembly: Ch13.exe
Token: 02000003
MethodTable: 00143834
EEClass: 0014145c
Name: Ch13.MainClass

Module: 00142e9c
Assembly: Ch13.exe
Token: 02000004
MethodTable: 001438b8
EEClass: 001414c8
Name: Ch13.ExceptionInMethodStateDescription

The MethodTable address of the MainClass is 00143834, and 001438b8 is the address for the ExceptionInMethodStateDescription class that was used in Step 2 to determine the associated Method Description table information for the MainClass and ExceptionInMethodStateDescription class.

2. Step 2: Retrieve the Method Description table information for the MainClass and ExceptionInMethodStateDescription classes using the MethodTable address from Step 1 using the dumpmt command in the windbg.exe. You can use 00143834 as the address of the MethodTable of the MainClass to determine the method description table for each of the methods defined in the MainClass (output might vary when you run it locally).

!dumpmt -MD 00143834
EEClass: 0014145c
Module: 00142e9c
Name: Ch13.MainClass
mdToken: 02000003
File: J:\Book\ExpertC#2012\SourceCode\BookExamples\Ch13\bin\Debug\Ch13.exe
BaseSize: 0xc
ComponentSize: 0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
54efa7e0 54cd4934 PreJIT System.Object.ToString()
54efe2e0 54cd493c PreJIT System.Object.Equals(System.Object)
54efe1f0 54cd495c PreJIT System.Object.GetHashCode()
54f81600 54cd4970 PreJIT System.Object.Finalize()
0014c015 0014382c NONE Ch13.MainClass..ctor()
002a0070 00143820 JIT Ch13.MainClass.Main(System.String[])

This output shows that MethodDesc Table address 00143820 is for the Main method of the MainClass class and so on.

In addition, you can use 001438b8 as the address of the MethodTable of the ExceptionInMethodStateDescription to determine the method description table for each of the methods defined in the ExceptionInMethodStateDescription class (output might vary when you run it locally).

!dumpmt -MD 001438b8
EEClass: 001414c8
Module: 00142e9c
Name: Ch13.ExceptionInMethodStateDescription
mdToken: 02000004
File: J:\Book\ExpertC#2012\SourceCode\BookExamples\Ch13\bin\Debug\Ch13.exe
BaseSize: 0xc
ComponentSize: 0x0
Slots in VTable: 8
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
54efa7e0 54cd4934 PreJIT System.Object.ToString()
54efe2e0 54cd493c PreJIT System.Object.Equals(System.Object)
54efe1f0 54cd495c PreJIT System.Object.GetHashCode()
54f81600 54cd4970 PreJIT System.Object.Finalize()
002a00f0 001438b0 JIT Ch13.ExceptionInMethodStateDescription..ctor()
002a0130 0014388c JIT Ch13.ExceptionInMethodStateDescription.MethodOne()
002a01a0 00143898 JIT Ch13.ExceptionInMethodStateDescription.MethodTwo()
002a0210 001438a4 JIT Ch13.ExceptionInMethodStateDescription.MethodThree()

The output shows that the Method Description table address for MethodOne, MethodTwo, and MethodThree is 0014388c, 00143898, and 001438a4, respectively, for the ExceptionInMethodStateDescription class, which will be used later in Step 3 to determine the exception-handling information associated with each of the methods for the ExceptionInMethodStateDescription class.

3. Step 3: Get the Exception Handling Block information for each of the methods associated with MainClass and ExceptionInMethodStateDescription class using the respective method description table addresses. For example, 00143820 is the method description table address of the Mainmethod of the MainClass, as retrieved in Step 2.

!EHInfo 00143820

The EHInfo command will return the exception-handling block information about the Main method of the MainClass class, as demonstrated in Figure 13-4.

images

Figure 13-4. Exception handling information of the Main method in the MainClass

Figure 13-4 shows that in the Main method there is no exception-handling block information associated by the C# compiler in the compile time as in Listing 13-3. The Main method hasn’t defined any exception-handling code (try...catch block) in the MainClass. For theExceptionInMethodStateDescription class, 0014388c, 00143898, and 001438a4 are the addresses for MethodOne, MethodTwo, and MethodThree, respectively, retrieved in Step 2.

!EHInfo 0014388c
!EHInfo 00143898
!EHInfo 001438a4

The EHInfo command will return the exception-handling block information for MethodOne, MethodTwo, and MethodThree of the ExceptionInMethodStateDescription class as shown in Figure 13-5.

images

Figure 13-5. Exception-handling information of the MethodOne, MethodTwo, and MethodThree of the ExceptionInMethodStateDescription class

Figure 13-5 demonstrates that the C# compiler added the exception-handling information for MethodOne with the IndexOutOfRangeException as the exception type, MethodTwo with the ArgumentException as the exception type, and Exception as the exception type for MethodThree for theExceptionInMethodStateDescription class.

In the .NET application, if you define any exception handler block in a method for the class, the C# compiler will then add an exception-handling information block for each of the methods defined in the class, as demonstrated in Figure 13-6. It shows each of the methods of the MainClassand ExceptionInMethodStateDescription class associated with the exception-handling information in it.

images

Figure 13-6. Exception-handling information of the MainClass and ExceptionInMethodStateDescription class

Exception Handling in .NET

When an exception occurs in a program, the CLR will handle this exception in one of three ways:

· It will search the exception-handling information array (if it exists) associated with the method to determine the related catch block or the handler block. If the CLR finds a matching one in the current method, it will then execute code from that handler block.

· Otherwise, it will continue to search for a relevant handler block to the calling method and so on.

· Finally, if the CLR does not find any matching exception handling block in the program, it will then log the stack trace information contained in the exception in the Windows log and abort the program, as demonstrated in Figure 13-7. The CLR will search for the exception-handling information from MethodA if an exception occurred in MethodA and continue searching MethodB to MethodC until it finds any information. If it finds an exception, it will execute and return from the program or dump the exception information in the Windows log as an unhandled exception.

images

Figure 13-7. Exception-handling information searching from Method State to Method State

So far, we have seen the way CLR handles an exception in a program in the place it occurred. There are many possibilities where you do not want to handle the exception in the place where it occurred but instead want to pass back this exception information to the caller of this method.Figure 13-8 shows what happens when a method call sequence (MethodA calls MethodB and MethodB calls MethodC) has happened.

images

Figure 13-8. Method call bubble up when an exception has occurred

Let’s modify the code in Listing 13-1 to do the following:

· The exception is handled by the method itself.

· The exception is handled by the CLR.

So the first version of this modified division program will handle the exception itself using the exception-handler block, and second version will not handle the exception by itself but rather let the application or Windows handle the exception, as demonstrated in Listing 13-4.

Listing 13-4. First Version of the Exception Handling

/* First version of the Exception handling */

using System;

namespace Ch13
{
class MainClass
{
static void Main(string[] args)
{
Division div = new Division();
div.Divide(100, 0);
}
}
public class Division
{
public int Divide(int a,
int b)
{
try
{
return a / b;
}
/* Exception is handled in here by catching all the exception. */
catch (Exception exception)
{
Console.WriteLine(
exception.StackTrace);
return -1;
}
}
}
}

This would be the second version of the exception handling:

using System;

namespace Ch13
{
class MainClass
{
static void Main(string[] args)
{
Division div = new Division();
div.Divide(100, 0);
}
}

public class Division
{
public int Divide(int a,
int b)
{
return a / b;
}
}
}

When the first version of the program from Listing 13-4 is compiled, the C# compiler will create an exception entry for the Method State Description table of the Divide method of the Division class. In execution of the first program from Listing 13-4, if any exception occurs in the Divide method, the CLR will then search the Method State Description table for this method to determine the exception-handler block information (if any exists). It will execute it and show the StackTrace information of the exception by accessing the exception object and return and finish the program execution, as shown in Figure 13-9.

images

Figure 13-9. Exception handled by the program itself

For the second version of the program in Listing 13-4, the compiler will not create any ExceptionEntry (it will create an empty array for the exception handling) for the Method State Description table of the Divide method of the Division class. There is no defined exception handler for this method. In execution of the second program from Listing 13-4, if any exception occurs the CLR will search the Method State Description table for this method but it will not find an exception handler associated with it. This program will then crash and let Windows take care of this exception by writing exception information to the Event Viewer, as demonstrated in Figure 13-10.

images

Figure 13-10. Windows Logs in the Event Viewer

From Figure 13-10 you can see that the CLR wrote the exception details to the Windows log and the Windows Error Reporting also takes care of it by writing the exception details into the Windows log.

Exception Handling and Bubble Up

As shown in Figure 13-8, the CLR will search through each of the methods in the method chain to match an appropriate exception-handler block. If it finds one, it will execute it or otherwise continue searching. Listing 13-5 presents an example of this.

Listing 13-5. Example of Salary Calculation

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
try
{
Salary salary = new Salary();
salary.CalculateSalary(10, 0);
}
catch (Exception exception)
{
Console.WriteLine("An error occured.");
}
}
}

public class Salary
{
public int CalculateSalary(int week, int rate)
{
try
{
Calculator calculator = new Calculator();
return week * calculator.Divide(week, rate);
}
catch (DivideByZeroException divideByZeroException)
{
throw;
}
}
}

public class Calculator
{
public int Divide(int a, int b)
{
try
{
return a / b;
}
catch
{
throw;
}
}
}
}

In Listing 13-5, each of the handler blocks defined in the Calculator and Salary classes handles the exception by returning the exception details from the Divide method to the CalculateSalary method of the Salary class, which is finally return to the Main method of the Program class. This whole concept is called the bubble up, where the exception is returned all the way up to the caller of the method from where the exception was raised. Figure 13-11 illustrates the bubble up concept using stack trace information extracted in the runtime while the code in Listing 13-5 is executing.

images

Figure 13-11. Exception Bubble up

When the exception occurred in the Divide method of the Calculator class in Listing 13-5, the CLR produced an instance of the DivideByZeroException type with the details shown in Figure 13-11. The CLR then passes these exception details back to the CalculateSalary method of the Salary class. The CLR modifies the StackTrace to combine the information it gets from the Exception object DivideByZeroException from the divide method and passes this back to the Main method of the Program class. The CLR will modify the StackTrace of the Exception object to capture the most up-to-date information about the exception and handle the exception as if there were a handler block defined.

While the CLR executes the program in Listing 13-5, the program pointer will move into the Divide method of the Calculator class. As soon as an exception is raised, the CLR will bubble up this exception all the way to the Main method of the Program class, which can be seen by looking at the stack trace of this call using windbg.exe, as demonstrated here:

0:000> !clrstack -a
OS Thread Id: 0xd08 (0)
Child SP IP Call Site
0039ee08 00400295 Ch13.Calculator.Divide(Int32, Int32) [J:\Book\ExpertC#2012\SourceCode\
BookExamples\Ch13\Program.cs @ 40]
PARAMETERS:
this (0x0039ee10) = 0x0153b688
a (0x0039ee0c) = 0x0000000a
b (0x0039ee3c) = 0x00000000
LOCALS:
0x0039ee08 = 0x00000000

0039ee40 004001d4 Ch13.Salary.CalculateSalary(Int32, Int32) [J:\Book\ExpertC#2012\SourceCode
\BookExamples\Ch13\Program.cs @ 28]
PARAMETERS:
this (0x0039ee60) = 0x0153b67c
week (0x0039ee5c) = 0x0000000a
rate (0x0039ee8c) = 0x00000000
LOCALS:
0x0039ee4c = 0x0153b688
0x0039ee48 = 0x00000000
0x0039ee58 = 0x00000000

0039ee90 004000d7 Ch13.Program.Main(System.String[]) [J:\Book\ExpertC#2012\SourceCode\
BookExamples\Ch13\Program.cs @ 12]
PARAMETERS:
args (0x0039eea0) = 0x0153b63c
LOCALS:
0x0039ee9c = 0x0153b67c
0x0039ee98 = 0x00000000

0039f0fc 5a8a21db [GCFrame: 0039f0fc]

This windbg.exe output shows the stack information while CLR executes the program in Listing 13-5. The stack of each of the methods is stacked one upon the other, for instance, CalculateSalary is on top of the Main method and Divide is on top of the CalculateSalary method. If any exception occurs, for example, in the Divide method, the CLR would search for the exception-handling information in that method, and if it cannot find it, it would then search CalculateSalary and lastly Main as they are stacked. Figure 13-12 demonstrates the exception searching and bubble up after an exception occurred in the Divide method of the Calculator class.

images

Figure 13-12. Example of the Exception bubble up in code

Figure 13-12 demonstrates that the program pointer has come back to the exception handler section of the Main method of the Program class from the Divide method of the Calculator class.

Protected Block

A protected or guarded block is declared using the try keyword; the handler block is defined using the catch or finally keyword. A catch block is declared using the catch keyword. This specifies the type of exception object the clause is going to handle and the handler code itself. A finally block is declared using the finally keyword. Using these try, catch, and finally statements, you can handle all the exceptional behavior of any program in the .NET application. The general structure of the try...catch statements while using a .NET program is shown in Figure 13-13.

images

Figure 13-13. Handler and protected block defined in the class

From Figure 13-13, you can see that the protected block is defined using the try block, and the handler block is defined using the catch and finally blocks. An example of the declaration of the protected and handler blocks is shown in Listing 13-6.

Listing 13-6. An Example of try.catch.finally in .NET

using System;

namespace Ch13
{
class MainClass
{
static void Main(string[] args)
{
ExampleOfExceptionUsage eeu = new ExampleOfExceptionUsage();
}
}

public class ExampleOfExceptionUsage
{
public void DoSomething()
{
try
{
/* Do some operation */
}

catch (ArgumentException argumentException)
{
/* handle ArgumentException exception */
}

catch (IndexOutOfRangeException indexOutOfRangeException)
{
/*handle IndexOutOfRangeException */
}

catch (Exception exception)
{
/* handle exception in general in here if ArgumentException
* and IndexOutOfRangeException did not occurred. */
}

finally
{
Console.WriteLine("It will execute all the time");
}
}
}
}

In the code in Listing 13-6, the try block is the place where we need to put the entire operation block, which will do the actual functional operation of the method. This is referred to as the protected or guarded block. In the protected block, if there is any situation where the argument has not been provided, the index of the array is not in range, or any other kind of unexpected behavior, the program will handle that unexpected behavior using the catch block or the finally block, referred to as handler blocks. However, the finally block will execute regardless of the exceptional behavior raised in the method. To explain how the code in Listing 13-6 works, let’s look at the decompiled IL code in the Listing 13-7, extracted using ildasm.exe from the executable produced by Listing 13-6.

Listing 13-7. IL Code of the DoSomething Method Defined in Listing 13-6

.method public hidebysig instance void DoSomething() cil managed
{
// Code size 39 (0x27)
.maxstack 1
.locals init (
[0] class [mscorlib]System.ArgumentException
argumentException,
[1] class [mscorlib]System.IndexOutOfRangeException
indexOutOfRangeException,
[2] class [mscorlib]System.Exception
exception)
IL_0000: nop
.try
{
.try
{
IL_0001: nop
IL_0002: nop
IL_0003: leave.s IL_0014
} // end .try
catch [mscorlib]System.ArgumentException
{
IL_0005: stloc.0
IL_0006: nop
IL_0007: nop
IL_0008: leave.s IL_0014
} // end handler
catch [mscorlib]System.IndexOutOfRangeException
{
IL_000a: stloc.1
IL_000b: nop
IL_000c: nop
IL_000d: leave.s IL_0014
} // end handler
catch [mscorlib]System.Exception
{
IL_000f: stloc.2
IL_0010: nop
IL_0011: nop
IL_0012: leave.s IL_0014
} // end handler
IL_0014: nop
IL_0015: leave.s IL_0025
} // end .try

finally
{
IL_0017: nop
IL_0018: ldstr "It will execute all the time"
IL_001d: call void [mscorlib]System.Console::WriteLine(string)
IL_0022: nop
IL_0023: nop
IL_0024: endfinally
} // end handler

IL_0025: nop
IL_0026: ret
} // end of method ExampleOfExceptionUsage::DoSomething

In this IL code, you can see that there are two blocks of try...catch, one for the inner execution block and the exception-handler block and the other for the outer try...catch, which is executed by the finally block regardless of whether the exception is raised. Let’s analyze the code carefully to really understand what’s going on in this IL code.

Where no unexpected behavior has occurred in the program, the CLR will execute the IL code as described here:

· The program will execute IL instruction from the label IL_0000 and keep executing until IL_0003, where it will find the instruction leave.s IL_0014, which will move the program control to the label IL_0014 as well as look for the surrounding finally block. However, there is none, so it will move the program control to the IL_0014 label.

· The program control will move into the IL_0014 and continue to the IL_0015, where it will find the instruction leave.s IL_0025. While executing the leave.s IL_0025 instruction, it will try to determine if there is any matching finally block. In this case there is one, so it will execute that first and then move the program pointer to the IL_0025 label and continue the program from there.

Where an exception occurs in the program, the CLR will execute the above IL code as described here:

· The program will execute IL instruction from the label IL_0000 and keep executing until IL_0003. While the instruction from IL_0000 to IL_0003 is executing, if any unexpected behavior is raised by the CLR, it will then instantiate a relevant exception object and try to determine if there is any matching handler section that is accepting the type of exception object the CLR is trying to match with. If there is, then the program will move to that point. For example, if the CLR instantiated an object of ArgumentException, then the program pointer will move into the IL_0005 and start executing instructions from that point up to IL_0008. In this location the CLR will find the leave.s IL_0014 instruction, from which it will then look for the related finally block to execute or else move the program control into IL_0014. Alternatively, if there is no matching exception object defined in the exception-handler section in the program, then it will execute the catch (Exception exception) handler section and continue the execution from there.

· When the program finishes executing the handler section of the program, it will continue to execute the instruction and it will find the IL_0015: leave.s IL_0025 instruction. This leave.s instruction will try to determine if there is any matching finally block, and if so it will execute that first and then move the program pointer to the IL_0025 and continue the program from there.

In both circumstances, the CLR will end up executing the IL_0025 and IL_0026 labels, regardless of the exception handler’s finally block, which will execute anyway.

Throw and Rethrow

The throw statement from the C# code and the rethrow IL instruction the C# compiler generates are used in the .NET application to rethrow an exception. The throw and rethrow instructions can be define as:

· The throw instruction throws the exception object on the stack.

· The rethrow instruction is permitted within the body of a catch handler, and it throws the same exception that was caught by this handler. The rethrow IL instruction does not change the stack trace in the object.

All the examples presented so far have used the exception raised by the CLR while executing the program to determine any exceptional behavior not expected while running the program. Let’s see how an exception can be raised in a program using the throw and rethrow statements, which is demonstrated in Listings 13-8 and 13-9.

Listing 13-8. Example of Throw Statement

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
throw new Exception("An exception object");
Console.WriteLine("This will never execute.");
}
}
}

While executing this program, the CLR creates an instance of the Exception type with the An exception object string literal. Using the throw instruction, the CLR raises an exception using the exception object, instantiating an instance of the Exception type. If you examine the following IL code from the executable of the program in Listing 13-8 generated using the .NETReflector program, you will see the C# compiler used the throw IL instruction to throw an exception, as shown in Listing 13-9.

Listing 13-9. IL Code While Using Throw Statement

.class private auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void
.ctor() cil managed {}

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
L_0000: nop
L_0001: ldstr "An exception object"
L_0006: newobj instance void [mscorlib]System.Exception::.ctor(string)

/* CLR will raise an Exception using the exception instance
* created in L_0006*/
L_000b: throw
}
}

In the IL code, the L_000b label has the throw IL instruction, which will instruct the program to throw the exception instance created in L_0006 label. On the other hand, rethrow is not directly usable in the C# code or it is not a C# keyword, because it is an IL instruction that will be generated by the C# compiler while the compiler compiles a particular pattern of the C# code for the exception handling block, as demonstrated in Listing 13-10.

Listing 13-10. An Example of Divide Operation

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
try
{
ExampleOfRethrow();
}
catch (Exception exception)
{
Console.WriteLine(exception.StackTrace);
}
}

static void ExampleOfRethrow()
{
try
{
int a = 10, b = 0;
var result = a / b;
}
catch
{
throw;
}
}
}
}

In Listing 13-10, you will see that in the handler block there hasn’t been anything done besides catching the exception object and letting the exception go by using the throw statement to be handled by the caller of this method. The decompiled IL code shown in Listing 13-11 of the executable of the Listing 13-10 demonstrates how the C# compiler used the rethrow instruction to rethrow the existing exception.

Listing 13-11. IL Code for the Code in Listing 13-10

.class private auto ansi beforefieldinit Ch13.Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 30 (0x1e)
.maxstack 1
.locals init ([0] class [mscorlib]System.Exception exception)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: call void Ch13.Program::ExampleOfRethrow()
IL_0007: nop
IL_0008: nop
IL_0009: leave.s IL_001c

} // end .try
catch [mscorlib]System.Exception
{
IL_000b: stloc.0
IL_000c: nop
IL_000d: ldloc.0
IL_000e: callvirt instance string
[mscorlib]System.Exception::get_StackTrace()
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: nop
IL_001a: leave.s IL_001c

} // end handler
IL_001c: nop
IL_001d: ret
} // end of method Program::Main

.method private hidebysig static void ExampleOfRethrow() cil managed
{
// Code size 20 (0x14)
.maxstack 2
.locals init ([0] int32 a,
[1] int32 b,
[2] int32 result)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldc.i4.s 10
IL_0004: stloc.0
IL_0005: ldc.i4.0
IL_0006: stloc.1
IL_0007: ldloc.0
IL_0008: ldloc.1

/* The CLR does the div operation
* 1. if successful then it will store the result at position
* 2 of method stack 2. Or otherwise move the execution in IL_00e */
IL_0009: div

/*It stores the result at position 2 of method stack */
IL_000a: stloc.2

IL_000b: nop
IL_000c: leave.s IL_0012

} // end .try
catch [mscorlib]System.Object
{
/* If any exception raised by the CLR while doing the div operation
* it creates an Exception (Or relevant type) object with the
* exception details on the Heap and put that exception object
* reference on top of the Evaluation Stack. Following pop
* instruction loads that exception object from the Evaluation Stack and
* rethrow it inIL_0010*/
IL_000e: pop

IL_000f: nop

/* This rethow instruction does not reset the stack trace of
* the original exception object*/
IL_0010: rethrow

} // end handler
IL_0012: nop
IL_0013: ret
} // end of method Program::ExampleOfRethrow

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
/* code removed */
} // end of method Program::.ctor

} // end of class Ch13.Program

From this IL code, the IL_0010: rethrow instruction will rethrow the current exception without altering the starting point of the exception object. Another usage of the throw statement is presented in the code given in Listing 13-12.

Listing 13-12. An Example of Divide Operation

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
try
{ ExampleOfRethrow(); }

catch (Exception exception)
{ Console.WriteLine(exception.StackTrace); }
}

static void ExampleOfRethrow()
{
try
{
int a = 10, b = 0;
var result = a / b;
}
catch (Exception ex) { throw; }
}
}
}

In this example, the ExampleOfRethrow method is doing a division operation and protecting that code by handling the unexpected behavior using the catch block. This catch block will accept an exception object of Exception type. In the catch block, I use the throw statement, which will actually rethrow the original exception instance created by the CLR in runtime without altering the starting point of the exception. Let’s analyze the IL code in Listing 13-13, generated from Listing 13-12 using ildasm.exe.

Listing 13-13. IL Example of Throw in IL Code

.class private auto ansi beforefieldinit Ch13.Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 30 (0x1e)
.maxstack 1
.locals init ([0] class [mscorlib]System.Exception exception)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: call void Ch13.Program::ExampleOfRethrow()
IL_0007: nop
IL_0008: nop
IL_0009: leave.s IL_001c

} // end .try
catch [mscorlib]System.Exception
{
IL_000b: stloc.0
IL_000c: nop
IL_000d: ldloc.0
IL_000e: callvirt instance string
[mscorlib]System.Exception::get_StackTrace()
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: nop
IL_001a: leave.s IL_001c

} // end handler
IL_001c: nop
IL_001d: ret
} // end of method Program::Main

.method private hidebysig static void ExampleOfRethrow() cil managed
{
// Code size 20 (0x14)
.maxstack 2
.locals init ([0] int32 a,
[1] int32 b,
[2] int32 result,
[3] class [mscorlib]System.Exception ex)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldc.i4.s 10
IL_0004: stloc.0
IL_0005: ldc.i4.0
IL_0006: stloc.1
IL_0007: ldloc.0
IL_0008: ldloc.1

/* The CLR does the div operation
* 1. if successful then it will store the result on top of the
* Evaluation Stack
* 2. Or otherwise move the execution in IL_000e*/
IL_0009: div

/* If div operation successful pop the output of the div from
* the Evaluation Stack and store into result (at position 2
* of the Locals section of this method stack) .*/
IL_000a: stloc.2
IL_000b: nop
IL_000c: leave.s IL_0012

} // end .try
catch [mscorlib]System.Exception
{
IL_000e: stloc.3
IL_000f: nop

/* If any exception raised by the CLR while doing the div operation
* it creates an Exception object (Or relevant type) with the
* exception details on the Heap and re-throw the original
* exception (without reset the original stack trace) in IL_0010.*/
IL_0010: rethrow

} // end handler
IL_0012: nop
IL_0013: ret
} // end of method Program::ExampleOfRethrow

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
/* code removed */
} // end of method Program::.ctor
} // end of class Ch13.Program

The CLR will execute the IL code as demonstrated below:

1. Step 1: The program will call the ExampleOfRethrow method from the IL_0002 of the Main method, and the program will move to the method ExampleOfRethrow and continue by executing the instruction from there.

2. Step 2: If everything goes normally in the ExampleOfRethrow method, then the CLR stores the div result in IL_000a and returns from that method back to the Main method to finish the execution of the program.

3. Step 3: If the execution from the IL_0009 does not go normally, then the CLR will instantiate a relevant exception object on the Heap and put that exception reference on top of the evaluation stack. The program will come to IL_0010, which loads the current exception details from the top of the evaluation stack, and rethrows the exception, which is caught by this handler without modifying the starting point of the exception. The CLR will then pass this exception to caller.

If you modify the code in Listing 13-12 as shown in Listing 13-14, the C# compiler will then generate different IL instructions, as shows in Listing 13-15.

Listing 13-14. Modified Version of the Code in Listing 13-12

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
try
{ ExampleOfRethrow(); }
catch (Exception exception)
{ Console.WriteLine(exception.StackTrace); }
}

static void ExampleOfRethrow()
{
try
{
int a = 10, b = 0;
var result = a / b;
}
catch (Exception ex)
{
throw ex;
}
}
}
}

In the code in Listing 13-14, the throw ex statement is used instead of using just the throw statement. While the CLR executes the throw ex statement, it will override some of the property of the exception object, as shown in Listing 13-15.

Listing 13-15. IL for the Code in Listing 13-14

.class private auto ansi beforefieldinit Ch13.Program
extends [mscorlib]System.Object
{
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 30 (0x1e)
.maxstack 1
.locals init ([0] class [mscorlib]System.Exception exception)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: call void Ch13.Program::ExampleOfRethrow()
IL_0007: nop
IL_0008: nop
IL_0009: leave.s IL_001c

} // end .try
catch [mscorlib]System.Exception
{
IL_000b: stloc.0
IL_000c: nop
IL_000d: ldloc.0
IL_000e: callvirt instance string
[mscorlib]System.Exception::get_StackTrace()
IL_0013: call void [mscorlib]System.Console::WriteLine(string)
IL_0018: nop
IL_0019: nop
IL_001a: leave.s IL_001c

} // end handler
IL_001c: nop
IL_001d: ret
} // end of method Program::Main

.method private hidebysig static void ExampleOfRethrow() cil managed
{
// Code size 20 (0x14)
.maxstack 2
.locals init ([0] int32 a,
[1] int32 b,
[2] int32 result,
[3] class [mscorlib]System.Exception ex)
IL_0000: nop
.try
{
IL_0001: nop
IL_0002: ldc.i4.s 10
IL_0004: stloc.0
IL_0005: ldc.i4.0
IL_0006: stloc.1
IL_0007: ldloc.0
IL_0008: ldloc.1

/* The CLR does the div operation
* 1. if successful then it will store the result on top of the
* Evaluation Stack
* 2. Or otherwise move the execution into IL_000e*/
IL_0009: div

/* Stores the output of the div operation from the evaluation stack into
* the result (at position 2) of the Locals section of this method
* stack. */
IL_000a: stloc.2
IL_000b: nop
IL_000c: leave.s IL_0012

} // end .try
catch [mscorlib]System.Exception
{
/* When any exception occurred, the CLR will store that exception
* from the evaluation stack into the ex object of the Locals
* section of this method stack. */
IL_000e: stloc.3
IL_000f: nop

/* Load the local variable at position 3 from the Local section
* of this method stack which is ex on the top of the evaluation
* stack */
IL_0010: ldloc.3

/* The CLR will throw exception using the ex object store
* into the Locals section at position 3 which will reset the
* starting point of the original exception and the CLR will
* set the exception as it raised from here instead of
* the original location.*/
IL_0011: throw

} // end handler
IL_0012: nop
IL_0013: ret
} // end of method Program::ExampleOfRethrow

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
/* code removed */
} // end of method Program::.ctor
} // end of class Ch13.Program

The CLR will execute this IL code as described here:

1. Step 1: The program will call the ExampleOfRethrow method from the IL_0002 of the Main method of the Program class and will move to the ExampleOfRethrow method and continue by executing the instruction from there.

2. Step 2: If everything goes normally, then the IL_000c: leave.s IL_0012 instruction will be executed from the ExampleOfRethrow method. At this time, the leave.s instruction will not find any finally block, so it will move to the IL_0012: nop and continue from there.

3. Step 3: If the div operation in IL_0009 from the ExampleOfRethrow method does not go normally, the program will come to the IL_000e, which will load the current exception details from the evaluation stack and store it in the local variable at position 3 of the ExampleOfRethrow method. In the instruction L_0010: ldloc.3 load, the value from the local variable at position 3 is moved into the top of the evaluation stack. The CLR then throws it (in the IL_0011) by resetting the starting point of the original exception raised position, in other words, it overwrites the stack information. You will see an example of the stack overwrite in the next section.

Figure 13-14 demonstrates how the CLR changes the exception information while using the following code:

catch(Exception ex)
{
throw;
}
catch
{
throw;
}

OR

catch(Exception ex)
{
throw ex;
}

images

Figure 13-14. Example of just throw

The CLR changes the exception details, and the caller of this method will not get the right information or the information about the original starting point of the exception, which will make the application debugging a bit more difficult. So use of the just throw statement will not change the starting point of the exception, whereas using the throw exceptionObject statement will change the starting point of the exception, as discussed above.

Stack Overwrite

Let’s look at an example where we will see how CLR overwrites the exception details. This program will give the month name based on the month index in the year, as shown in Listing 13-16.

Listing 13-16. An Example of Stack Overwrite

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
try
{
Year year = new Year();
year.GetMonth(22);
}
catch (Exception exception)
{
Console.WriteLine("{0}", exception.StackTrace);
}
}
}

public class Year
{
public string GetMonth(int position)
{
try
{
MonthNameFinder monthFinder = new MonthNameFinder();
return monthFinder.Find(position);
}
catch (Exception exception)
{
throw;
}
}
}

public class MonthNameFinder
{
private string[] months =
{
"January", "February",
"March", "April",
"May", "June",
"July", "August",
"September", "October",
"November", "December"
};

public string Find(int whichMonth)
{
try
{
return months[whichMonth];
}
catch (Exception exception)
{
throw;
}
}
}
}

Running this program will show the details provided in Figure 13-15.

images

Figure 13-15. While using only a throw statement

Listing 13-17 shows the modified version of the GetMonth method of the Year class.

Listing 13-17. Modified Code of the GetMonth(int position) of the Year Class

public class Year
{
public string GetMonth(int position)
{
try
{
MonthNameFinder monthFinder = new MonthNameFinder();
return monthFinder.Find(position);
}
catch (Exception exception)
{
throw exception;
}
}
}

The program will produce the exception details as shown in Figure 13-16.

images

Figure 13-16. While using the throw statement with exception object

How the CLR Matches Exception in Runtime

When you define exception-handling blocks in a program, you can use different types of the exception type as input of the catch blocks. These are called handler blocks. In runtime, the CLR will choose which block of the handler it should invoke (depending on the exception type) when an exception is raised, otherwise the CLR will choose the catch-handling block, which is defined in Exception as the type by default. If no matching catch-handling block is defined or found in the class, then the program control will bubble up all the way to the caller of the method to determine the best-matched exception type. This catch block searching or matching maintains the hierarchy (i.e., most of the derived exception types). In Listing 13-18, IndexOutOfRangeException is the defined catch block (the best match), and it will be the first one in the chain, and the topmost (for the following example, Exception) one will be the last one.

Listing 13-18. An Example of the Exception Type Matching for the Exception Handler Block

using System;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
Division div = new Division();
div.Divide(100, 0);
Console.ReadLine();
}

public class Division
{
public int Divide(int a, int b)
{
try
{
return a / b;
}

catch (IndexOutOfRangeException indexOutOfRangeException)
{
Console.WriteLine("IndexOutOfRangeException");
}
catch (ArgumentException argumentException)
{
Console.WriteLine("ArgumentException");
}
catch (DivideByZeroException divideByZeroException)
{
Console.WriteLine("{0}\n{1}",
"DivideByZeroException",
"This handler block will execute.");
}
catch (Exception exception)
{
Console.WriteLine("{0}",
"It will execute when there is no best matched found.");
}
return -1;
}
}
}
}

So while this program executed by the CLR will raise the exception in the Divide method, as shown in Listing 13-18, it will try to match it with the relevant exception-handler block, as demonstrated in Figure 13-17.

images

Figure 13-17. Exception matching when an exception occurred in a method

In the program in Listing 13-18, IndexOutOfRangeException, ArgumentException, DivideByZeroException, and Exception type have been used for the handler blocks. When the CLR raises an exception in the Divide method while executing the a/b statement with the value 10/0, for example, it will first search the handler block defined in the Divide method to match the exception type raised and the exceptions defined in the handler block to execute the code in the matched exception-handler block. The best match exception type is DivideByZeroException, so the CLR will execute the handler block for theDivideByZeroException and show the output:

DivideByZeroException This handler block will execute.

If you modify this program by removing the DivideByZeroException handler block and then execute the program, the CLR will try to match the best-matched handler block and execute the best-matched handler block, which is the Exception block, and produce the following output:

It will execute when there is no best matched found.

Unhandled Exception

If you remove the catch block that handled the Exception type exception in the Main method and Exception type and DivideByZeroException from the Divide method, when an exception occurs in the Divide method the CLR will try to match the raised exception with the catch block. However, it will not find any, as there is no longer a DivideByZeroException or Exception type handler block in the Divide method. The CLR will try to determine a possible handler block for the exception it raised. If there is any handler block defined in the caller of the method (e.g., here in the Main method of the Program class), the CLR will move the execution pointer to that point and try to match the type of the exception defined in the handler block. If it does not find a match, then the handler block will execute, as shown in Figure 13-18.

images

Figure 13-18. Exception matching when there is no match in executing method.

If there is no handler block defined in the Main method of the Program class or there is no best-matched exception type defined in the handler block of the Main method in the Program class, the CLR will then write the exception information to the Windows log.

Using Statement

The using statement in C# allows you to specify for the CLR when objects that use resources should release them. The object provided to the using statement must implement the IDisposable interface, and this interface provides the Dispose method. Listing 13-19 demonstrates the usage of the usingstatement.

Listing 13-19. Example of Using Statement

using System;
using System.Text;
using System.IO;

namespace Ch13
{
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder();
sb.Append("StringBuilder\n");

using (StringWriter sw = new StringWriter(sb))
{
sw.WriteLine("StringWriter");
}
Console.WriteLine("{0}", sb.ToString());
}
}
}

The decompiled IL code for the program in Listing 13-19 is shown in Listing 13-20.

Listing 13-20. IL Code for Listing 13-19

.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 77 (0x4d)
.maxstack 2
.locals init (
[0] class [mscorlib]System.Text.StringBuilder sb,
[1] class [mscorlib]System.IO.StringWriter sw,
[2] bool CS$4$0000)

/* Code removed */

IL_0013: ldloc.0
IL_0014: newobj instance void
[mscorlib]System.IO.StringWriter::.ctor(
class [mscorlib]System.Text.StringBuilder)
IL_0019: stloc.1
/* using statement has been replaced with the try as protected block
* and finally as the handler block */
.try
{
IL_001a: nop
IL_001b: ldloc.1
IL_001c: ldstr "StringWriter"
IL_0021: callvirt instance void
[mscorlib]System.IO.TextWriter::WriteLine(string)
IL_0026: nop
IL_0027: nop
IL_0028: leave.s IL_003a
} // end .try
finally
{
IL_002a: ldloc.1
IL_002b: ldnull
IL_002c: ceq
IL_002e: stloc.2
IL_002f: ldloc.2
IL_0030: brtrue.s IL_0039
IL_0032: ldloc.1
IL_0033: callvirt instance void
[mscorlib]System.IDisposable::Dispose()
IL_0038: nop
IL_0039: endfinally
} // end handler

/* Code removed */
}

The IL code of the label IL_0024 will look for the surrounding finally code block, and if it finds it, it will then execute that finally block and continue the execution from label IL_0030 until it finishes the execution.

Summary

In this chapter we have learned about the C# exception, the exception-handling mechanism, and how the CLR handles the exception in runtime. The C# compiler embeds the exception-handling–related information in the Method State Description table, which is later used by the CLR to handle exceptions. You have explored these methods in detail by analyzing the runtime Method State Description information of the methods defined in a type. The throw statement and rethrow IL instruction have been discussed in depth to explain different scenarios and explain how the CLR works differently in different circumstances. The chapter also explored the unhandled exception. The next chapter will discuss about the asynchronous programming using async and await.