Exceptions in C#

|
| By Webner

An exception is an unexpected or unwanted event that occurs when a program is executed i.e at runtime, that disrupts the normal flow of the program’s instructions. Sometimes while executing the program, a user might face the possibility that the program may crash or show an unexpected event during its runtime execution. This unwanted/unexpected occurrence of an event is known as an Exception and generally gives the indication that there is something wrong within the code.
An application might also encounter an error during the execution. When an error occurs, either CLR or program code throws an exception that contains necessary information about the error. There are two types of exceptions in C# .Net, the exceptions that are generated by the executing program, and the other exceptions are generated by the CLR.

Errors:

  • Errors are unexpected issues that might arise during the execution of a computer program.
  • Errors cannot be handled.
  • All Errors are exceptions.

Exceptions:

  • Exceptions are the unwanted/unexpected events that might be encountered during program runtime.
  • Exceptions can be handled using try-catch mechanisms.
  • All exceptions are not errors.

C# includes built-in classes for every possible exception. All the exception classes in C# are directly or indirectly derived from the base Exception class. In C# language, Exception (<System.Exception>) is the base class of all exceptions. And it itself is derived from the Object (<System.Object>) class.
There are two main classes for exceptions –

  • SystemException
  • ApplicationException

SystemException is a base class for all CLR generated errors.
ApplicationException serves as a base class for all the exceptions that are related to the application, which you want to raise on business rule violation.

SystemException class is a base class for all the exceptions that can occur during execution of the program.
No other class derives ApplicationException class by default, because a programmer needs to derive this class to create your own exception classes as per the business rules.

System-Level Exception:

  • System exceptions are derived from the base class System.SystemException which is also a derived class of Exception.
  • A System-level exception occurs when a recoverable error is encountered, for example, the wrong type of input data, arithmetic exceptions, etc.
  • These are the exceptions that are defined by the user, thrown by the application and mostly the program can resume back to its ordinary course of instructions after solving this type of exception.
  • Exceptions can be handled at the code level using try-catch blocks.

Application Level Exception:

  • Application-level exceptions are derived from the base class System.ApplicationException which in turn is also derived from the class of Exception.
  • These are basically, the custom exceptions that are dependent on the business logic of your application.
  • The programmer needs to write custom exception classes to handle these types of requirements.
  • If the code does not have a try and catch block for the occurred exception then it propagates to the page level, at page level the Page_Error routine can be used to handle the exception. If the error is still not handled at the page level then the error finally propagates to the Application level.
  • At the application level, we use the global.aspx file to overwrite Application_Error.

Important Exception Classes(System.Exception):

  • ArgumentException :- Raised when an invalid non-null argument is passed to a method.
  • ArgumentNullException :- Raised when a null argument is passed to a method.
  • ArgumentOutOfRangeException :- Raised when the value of an argument is outside the range of defined valid values for that method.
  • DivideByZeroException :- Raised when an integer value is divided by zero.
  • FileNotFoundException :- Raised when a physical file does not exist at the specified location.
  • FormatException :- Raised when a value is not in an appropriate format that can be converted from a string by a conversion method such as Parse.
  • IndexOutOfRangeException :- Raised when an array index is outside the lower or upper bounds of an array or collection.
  • InvalidOperationException :- Raised when a method call is invalid in an object’s current state.
  • InvalidCastException :- Raised when incompatible types are being converted.
  • KeyNotFoundException :- Raised when the specified key for accessing a member in a collection does not exist.
  • NotSupportedException :- Raised when a method or operation is not supported.
  • NullReferenceException :- Raised when programs access members of null objects.
  • OverflowException :- Raised when arithmetic, casting, or conversion operation results in an overflow.
  • OutOfMemoryException :- Raised when a program does not get enough memory to execute the code.
  • StackOverflowException :- Raised when a stack in memory overflows.
  • TimeoutException :- Raised when the time interval allotted to an operation has expired.

Every exception class in .Net is derived from the base Exception class. It includes the following important properties using which you can use to get information about the exception when you handle the exception.

  • Message :- Provides details about the cause of the exception.
  • StackTrace :- Provides information about where the error occurred.
  • InnerException :- Provides the information about the series of exceptions that might have occurred under the main wrapper exception.
  • HelpLink :- This property holds the help URL for a particular exception.
  • Data :- This property can hold arbitrary data in key-value pairs.
  • TargetSite :- Provides the name of the method where the exception was thrown.
    • When an error occurs, either the application code or the default handler handles the exception.
    • Exception is a base class for any type of exception class in C#.
    • SystemException class is used for CLR related runtime errors.

Exception Handling in C#

An exception is thrown by the program code or the CLR if there is an error in the program. These exceptions need to be handled to prevent the crashing of programs. C# provides built-in support to handle the exceptions using try, catch & finally block.
Exception handling in C# mainly revolves around the four keywords (try, catch, finally, throw).

Try Block:

The code should be put in the try block that may raise an exception followed by a catch or finally block. Variable declarations should be out of the try block so that they can be accessed in the catch and finally blocks.
We don’t want to show an exception message to the user and stop the execution. So, we need to handle exceptions using try, catch, finally block.
The try block simply tells the compiler to monitor the code for an exception. Exceptions raised within the try block must be handled using the catch block.
The try block must be followed by catch or finally or both blocks. The try block without a catch or finally block will give a compile-time error.
The try block can have any number of catch blocks. Just the most specific exception should precede the most generic one, i.e. all child catch blocks to be defined first followed by the parent catch block.

Catch Block:

Exceptions raised within the try block can be handled using the catch block. Code in the catch block will only execute when an exception occurs.
Multiple catch blocks can also be specified with a different exception type called exception filters. Multiple catch blocks are useful when you want to handle different exceptions in different ways.
A multiple catch block with the same exception type is not allowed. It will give a compile-time error.
Parameterless catch block and a catch block with an Exception parameter are not allowed in the same try-catch statements, because they both do the same thing. It will give a compile-time error.
Parameterless catch block catch{ } or general catch block catch(Exception){ } must be the last block. The compiler will give an error if you have other catch blocks after a catch{ } or catch(Exception) block.

Finally Block:

The final block must come after a try or catch block. The finally block will always be executed whether or not an exception is thrown. The finally block is generally used for cleaning-up code e.g. for disposing of unmanaged objects etc.
Multiple finally blocks are not allowed. Also, the finally block cannot have a return, continue, or break keywords. It doesn’t allow control to leave the final block.

Nested try-catch:

C# allows nested try-catch blocks. In the nested try-catch block, an exception will be caught in the catch block that follows the try block where an exception occurred. If there isn’t any inner catch block with the appropriate exception type, then the exception will flow to the outer catch block until it finds the appropriate exception filter.

  • Use the try, catch, and finally blocks to handle exceptions in C#.
  • The try block must be followed by a catch or finally block or both.
  • Multiple catch blocks are allowed with different exception filters. General catch{..} block must come last.
  • catch{..} and catch(Exception ex){ } both cannot be used.
  • The finally block must come after the try or catch block.
  • The finally block will always execute irrespective of whether an exception occurred or not.
  • The finally block is an appropriate place for disposing off objects.
  • The finally block cannot have a return or break because it isn’t allowed to leave the control.
  • Nested try-catch blocks are allowed in C#.
  • An Exception will be caught in the inner catch block if an appropriate filter is found, otherwise, it will be caught by the outer catch block.

Throw keyword:

An exception can be raised manually by using the throw keyword. Any type of exceptions that is derived from the Exception class can be raised using the throw keyword.
The throw creates an object of any valid exception type using the new keyword. The throw keyword cannot be used with any other type which does not derive from the Exception class.

Re-throwing an Exception:

We can also re-throw an exception from the catch block to pass on to the caller and let the caller handle it the way they want.
The catch block simply re-throws the exception using only the throw keyword (not throw e). This will be handled in the catch block in the caller method or main() method. The stack trace of this exception will give the full detail of where exactly this exception occurred.
If you re-throw an exception using an exception parameter then it will not preserve the original exception and creates a new exception.

Custom Exception Class:

A custom exception class can be created to raise an exception when the business rule of your application gets violated. A custom exception class can be created by deriving an Exception or ApplicationException class.

Best Practices for Exception Handling:

  • Clean Up Resources in a Finally Block
  • Handle common conditions without throwing exceptions
  • Design classes so that exceptions can be avoided
  • Exceptions should only be used if you can’t handle the situation in a decent manner
  • Document the Exceptions You Specify
  • Throw Exceptions With Descriptive Messages
  • Catch the Most Specific Exception First
  • Don’t Ignore Exceptions
  • Wrap the Exception :- Wrap the system exceptions with your custom exception.
  • End exception class names with the word Exception
  • Include a localized string message in every exception
  • Don’t Log and Throw :- Either log the exception or throw it but never do both. So, only catch an exception if you want to handle it. Otherwise, specify it in the method signature and let the caller take care of it.
  • Use finally blocks instead of catch blocks if you are not going to handle the exception
  • Place throw statements so that the stack trace will be helpful
  • Restore state when methods don’t complete due to exceptions
  • Never throw an exception from finally block
  • Pass all relevant information to exceptions to make them informative as much as possible
  • Always terminate the thread which it is interrupted
  • “Throw early catch late” principle:- This principle implicitly says that you will be more likely to throw it in the low-level methods, where you will be checking if single values are null or not appropriate. And you will be making the exception to climb the stack trace for several levels until you reach a sufficient level of abstraction to be able to handle the problem.

Leave a Reply

Your email address will not be published. Required fields are marked *