.NET structured exception handling is a technique for dealing with runtime exceptions.


Within the .NET nomenclature, an exception accounts for bugs, bogus user input, and runtime errors, even though programmers may view each of these as a distinct issue.


The Role of .NET Exception Handling

The building blocks of .NET Exception Handling

Programming with structured exception handling involves the use of 4 interrelated entities:


  1. A class type that represents the details of the exception.
  2. A member that throws an instance of exception class to the caller under the correct circumstances.
  3. A block of code on the caller's side that invokes the exception-prone member.
  4. A block of code on the caller's side that will process (or catch) the exception, should it occur.


The C# programming language offers 5 keywords (try,catch,throw,finally and when) that allow you to throw and handle exceptions.


The System.Exception Base Class

All exceptions ultimately derive from the System.Exception base class.

Property:

  1. Data: This read-only property retrieves a collection of key-value pairs (represented by an object implementing IDictionary) that provide additional programmer-defined information about the exception. By default, the collection is empty.
  2. HelpLink : This property gets or sets a URL to a help file or website describing the error in full detail.
  3. InnerException : This read-only property can be used to obtain information about the previous exceptions that caused the current exception to occur. The previous exceptions are recorded by passing them into the constructor of the most current exception.
  4. Message: This read-only property returns the textual description of a given error. The error message itself it set as a constructor parameter.
  5. StackTrace: This read-only property contains a string that identifies the sequence of calls that triggered the exception. As you might guess, this property is useful during debugging or if you to dump the error to an external error log.
  6. TargetSite: This read-only property returns a MethodBase object, which describes numerous details about the method that throw the exception (invoking ToString() will identify the method by name.)



Throwing a General Exception

You want to create and configure a new instance of the System.Exception class, setting the value of the read-only Message property via the class constructor.

throw new Exception("Okey!");


Catching Exceptions

When you are invoking a method that may throw an exception, you make use of a try/catch block. After you have caught the exception object, you are able to invoke the members of the exception object to extract the details of the problem.

try
 {
     // Code block
 }
catch (Exception e)
 {
     Console.WriteLine(e.Message);
     Console.WriteLine(e.TargetSite);
 }

In essence, a try block is section of statements that may throw an exception during execution. If an exception is detected, the flow of program execution is sent to the appropriate catch block.



System-Level Exceptions (System.SystemException)

Exceptions that are thrown by the .NET Platform are (appropriately) called System exceptions. These exceptions are generally regarded as nonrecoverable, fatal errors. System exceptions derive directly from a base class named System.SystemException, which in turn derives from System.Exception (which derives from System.Object).

Ex:

NullReferenceException nullEx = new(); // is-a SystemException


Application-Level Exceptions (System.ApplicationException)


public class ApplicationException : Exception { }


Like SystemException, ApplicationException does not define any additional members beyond a set of constructors. Functionally, the only purpose System.ApplicationException is to identify the source of the error.

When you handle an exception deriving from System.ApplicationException you can assume the exception was raised by the code base of the executing application, rather than by the .NET Core base class libraries or .NET runtime engine.


Processing Multiple Exceptions

try
 {
     // Code block
 }
catch (Exception ex1)
 {
     // ex1 exception
 }
catch(Exception ex2)
 {
     // ex2 exception
 }

General Catch Statements

C# also supports a "general" catch scope that does not explicitly receive the exception object thrown by a given member.

try
{
     // Code block
}
catch // general catch
{
throw;
}


Rethrowing Exceptions

When you catch an exception, it is permissible for the logic in a try block to rethrow the exception up the call stack to the previous caller. To do so, simply use throw within a catch block. This passes the exception up the chain of calling logic, which can be helpful if your catch block is only able to partially handle the error at hand.

try
{
}
catch (Exception)
{
throw;
}

The finally block

A try/catch scope may also define an optional finally block. The purpose of a finally block is to ensure that a set of code statements will always execute, exception (of any type) or not.

try
{
}
catch (Exception ex)
{
}
finally
{
     // will always work
}

Exception Filters

C# 6 introduced a new clause that can be placed on a catch scope, via the when keyword. When you add this clause, you have the ability to ensure that the statements within a catch block are executed only if same condition in your code hold true. This expression must evaluate a Boolean (true of false) and can be obtained by using a simple code statement in the "when" definition itself or by calling an additional method in your code, this approach allows you to add "filters" to your exception logic.

catch (CarIsDeadException e) when (e.ErrorTimeStamp.DayOfWeek != DayOfWeek.Friday)
{
throw;
}


"when" clause to the CarIsDeadException handler to ensure the catch block is never executed on a Friday.


public class CarIsDeadException : ApplicationException
{
    public DateTime ErrorTimeStamp { get; set; }
    public string CauseOfError { get; set; }
    public CarIsDeadException() { }
    public CarIsDeadException(string cause, DateTime time) : this(cause, time, string.Empty) { }
    public CarIsDeadException(string cause, DateTime time, string message) : this(cause, time, message, null) { }
    public CarIsDeadException(string cause, DateTime time, string message, Exception inner) : base(message, inner)
    {
        CauseOfError = cause;
        ErrorTimeStamp = time;
    }
}