Day 4 Programming in C# 70-483
Index
- Introduction
- Handling exceptions
- Throwing exceptions
- Custom exceptions
- References
Introduction
This post is part of a series of post to help you prepare the MCSD certification, particularly the certification exam 70-483, based on the book:You will find all the code available in the following GitHub repository. Lets talk a little bit about threads and how they work using a .Net development environment.
Handling exceptions
Exceptions are key point for your coding projects, by implementing a well designed exception handling system you will find out where the errors are in your system more easily and you'll get more efficient.
Exceptions are actually an object which contains a lot of useful information about what went wrong. They inherit from System.Exception and their structure is given by the reserved word: try and catch (optional). Your are allowed to define one or more catch blocks, so you can handle different types of exceptions. Keep in mind you have to declare those catch blocks from most-specific to less-specific, ie:
See here above how we are trying to parse a piece of text and effectively, we are getting a wrong format exception. On the other side, if the format were properly provided but another error raises like an overflow exception for instance, then the second catch block would be executed instead.
Since C#1, developers were allowed to create empty catch blocks to get exceptions from other languages like C++.
Sometimes, you might want to run a code always either an exception is raised or not. Typically this is done to release resources. Then you can define a finally block after the last catch statement.
In some scenarios you might want to prevent a finally block to be executed. You can achieve this by performing an Environment.FailFast and accessing one of its two implementations (one takes only an string, the other takes the string an the exception object). When this method is triggered the call is logged within the Windows application event log system and then the application is closed. Ie:
In this example, if the user introduces "10" as input, we'll be getting an exception and our app will be closed. When this happens, you can check some properties within the exception object like:
- StackTrace: contains a list with all the methods triggered to get to the current point.
- InnerException: this happens when a new exception is thrown because of another exception.
- Message: a human-readable message describing the exception.
- ...
Finally, bear in mind that your finally blocks can't raise any exception. If not, the finally block might not be completely executed and the control will be passed to the next try/catch block, if there's any.
Only catch exceptions when you can resolve the issue or log the error. Try to avoid general catch blocks at the lower layers of your apps. The logging should also done in the higher layers, that way you will avoid duplicating logging messages.
Throwing exceptions
Use the reserved word throw to trigger a particular exception, then the CLR will start looking for a try/catch block which will handle the error. You can throw an exception in three different ways:- Using throw without any identifier: this won't modify the call stack
- Using throw with the original exception: this will reset the call stack to the current location, so you lose from where the original exception came and will be harder to debug.
- Using throw with a new exception: this happens when you want to send a different exception to the caller. Imagine a calculator application which makes the arithmetic calls to a different engine. If something goes wrong in the math engine we'll end up with some kind of customized exception, ie: SumException. You don't want to send that back to the user, then you can instantiate a new exception object which contains relevant information for your users, but also links with the original exception.
There is a functionality added in C#5 which allows you to trigger an exception outside of the try/catch block with all the information related the exception. You need to call the ExceptionDispatchInfo.Throw() method and is generally used when you want to catch an exception in one thread and throw it on another thread. Is defined in the System.Runtime.ExceptionServices namespaces and it preserves the stack trace. See here below an example of the usage:
Notice how we capture the exception within the catch block, so then we can use it after the try/catch block. The code only fails if we don't introduce a number.
One thing you should be aware of is not to throw exceptions when dealing with expected situations. If the user introduces wrong data in a form that won't be the cause to throw an exception. You should have proper validation, besides exception affects slightly performance. Some exceptions are thrown only at runtime (ArithmeticException, DivideByZeroException,...) and you don't want to catch them. On the other side, you can catch more specific exceptions like:
- Exception
- ArgumentException
- ArgumentNullException
- ArgumentOutOfRAngeException
- FormatException
- InvalidOperationException
- NotImplementedException
- NotSupportedException
- ObjectDisposedException
Custom exceptions
A customized exception should inherit from System.Exception class and it needs at least a parameter less constructor. As a best practice you can add few other constructors:
- one that takes one string
- one that takes both a string and a exception
- one for serialization
By convention you have to add the "Exception" suffix when naming your exception class. The serialization attribute ([Serializable]) is important to work correctly across application domains.