Errors will happen. The question is how will you deal with them? The QA Friday from 2015 Dec-11 talked about this question. This episode explains C# exceptions and how they are different from C++ exceptions.
When should you use exceptions in your code? You might find some guidance that tells you to place a try/catch or a try/catch/finally block around any method that can throw an exception. I advise you to start a try block for either or both of the following reasons:
- You know how to handle an exception either completely or partially. As long as you have some meaningful contribution to make if you were to catch a specific exception type, then go ahead and setup the try block and add your catch statement to catch that exception.
- You have some code that you want to make sure always runs either with to without an exception. Then setup the try block and add a finally statement so you can place your code in the finally block. This will run either after all the code in the try block finishes normally or after an exception is thrown and potentially caught by one of the exception handlers.
In C#, you can throw anything that derives from System.Exception including System.Exception itself. I advise against throwing System.Exception directly. It doesn’t provide any additional type information and creating your own type is just too easy. There are also many useful and recommended exception types already defined for you in the .Net Framework. Here are some of the common types:
- InvalidOperationException – Throw this type when you want to show that a method should not be called while the class instance is in a particular state.
- ArgumentException – Throw this when you find an argument passed to one of your methods that’s not right and not due to one of the next two scenarios.
- ArgumentNullException – Throw this when you find an argument passed to one of your methods that’s not right because it’s null.
- ArgumentOutOfRangeException – Throw this when you find an argument passed to one of your methods that’s not right because its value is either too small or too large.
- NullReferenceException – Don’t throw this. Just fix the problem. This exception represents a coding bug that needs to be fixed. You might want to catch this exception to log the problem.
- OutOfMemoryException – Don’t throw this exception either. If you catch this exception, then you might be able to recover by getting rid of memory you don’t need and trying the operation again. The computer system is likely very unstable at this point so you may not be able to recover.
Listen to the full episode or read the full transcript below.
Exceptions in C# are even more integrated into the language than in C++. This is because they’ve been part of the C# language from the very beginning.
You might find guidance in other places and even in the documentation from Microsoft that tells you to use a try block around any method that could throw an exception. You should really only begin a try block for these two reasons:
◦ #1 Is whenever you know how to handle an exception. This doesn’t mean that you have to fully handle the exception because you can still rethrow an exception if you want to let it be caught again at some other location. But if you have some value that you can add to the program by catching one or more exception types, then go ahead and start a try block so you can add your catch statements.
◦ #2 Is whenever you need some code to run always no matter what. You see, whenever an exception is thrown, the code that comes after that gets skipped. It’s like when you throw a ball over other people so they can’t reach it. When you have code that needs to run no matter if an exception is thrown or not, then you need a finally block.
The structure of try/catch/finally starts with the keyword try and then opening and closing curly braces. Any code inside the curly braces that throws an exception will potentially be eligible for catching that exception if there is a matching catch block after the try block.
After the try block, you can have as many catch blocks as you want. Each catch block begins with the keyword catch and then an exception type in parenthesis after catch. You can also give the exception a name almost like a method parameter and if you do this, then you can refer to the exception if it’s caught and read various properties. You might decide after catching an exception based on this information whether or not you really can handle the exception. If you can’t handle the exception for whatever reason, you can just rethrow the exception.
Let’s say you catch a PathTooLong exception. On Windows machines, there’s still a limitation to how long a path can be to a file on the file system. If you exceed this limit, then code inside the .Net Framework will throw a PathTooLong exception. Your catch block reads like this:
◦ catch (PathTooLongException ex)
◦ The keyword catch identifies this as a catch statement and the PathTooLongException is the type of exception that this catch statement will try to catch. The name ex is how you can refer to the exception instance that was caught.
◦ If you want to rethrow this exception, it’s tempting to write:
▪ throw ex;
◦ But this will throw a new PathTooLong exception from this point. To rethrow the exception that was caught and leave it unmodified, just write:
So far all of this is identical to C++ except for a couple differences that I’ll explain right after this message from our sponsor.
( Message from Sponsor )
The first difference is not obvious but has to do with what types can be thrown. In C++, you can throw almost anything. Want to throw an integer with the value 5? Go ahead. But in C#, anything that is thrown must ultimately derive from the System.Exception class. You can throw a new instance of the Exception class itself but I don’t recommend that. It’s very easy to either throw one of the predefined exception types or create your own. If you create your own exception type, just create a class that derives from System.Exception. It’s good programming practice to name your new exception class so that it ends with Exception.
There are some additional rules that you need to follow if you want to throw your own custom exception from one app domain to another app domain but since we haven’t talked about app domains yet, I’ll save that for a future episode.
The second difference has to do with the finally block. This is an optional statement that comes after all the catch statements. You can also have just a try statement followed by a finally statement with no catch statements at all. The finally block starts with the keyword finally and goes right into the opening and closing curly braces that define the body of the finally block.
This is code that will run after any catch block. If there’s no catch block at all or no catch block that matches the specific exception type that was thrown, then the code in finally will still run before the stack gets unwound and the next try/catch/finally statements get their chance to run.
The finally block is your chance to run code that you can use to clean up resources. You might wonder why this is necessary. I mean doesn’t C# have this amazing garbage collector? You don’t have to worry about freeing memory that you allocated in C#. Just forget about it and let the system clean it up when needed. The same thing with a file handle, right? Just forget about it and let the system clean it up eventually. the problem with this is eventually. You have no control over when that file handle or that database connection will eventually get closed.
That’s why you need finally. Together with the IDisposable interface and the Dispose method, you can call Dispose at just the right time while you still have a valid reference to the instance. This gives you the ability to close that file handle or close that database connection right away. This could be important because maybe some catch block will want to retry the same operation and will need to open that file or database again. If your previous zombie instance still has that resource open, then you may not be able to open it again.
It’s interesting that while finally is critical to working effectively with a garbage collector that runs on its own schedule, you won’t find finally anywhere in a C++ program. This whole concept just doesn’t exist in C++. Why?
Because in C++, objects get deleted the moment they go out of scope. An exception causes the code that would have followed to be skipped, but the C++ language guarantees that any object instances that have already been instantiated will get deleted the moment either normal code flow or an exception causes them to go out of scope. so even though that ball is being thrown over so many code statement that will never get the chance to run, destructors will still be called. This gives you the precise control you need to clean up any resources automatically at the right time.
The C++ language actually makes it easier to work with exceptions and resources than C# with its finally feature. You see, object-oriented programming is all about encapsulating behavior and data so that a consumer has a consistent and well defined interface to work with. Making other code call a method so that the class can do its own job effectively breaks this design. What if the other code forgets to call Dispose? Or didn’t realize that it needed to call Dispose at all?
The garbage collector is nice but you can get the same results and actually more predictable results from C++ without making some other code clean up after your class just because it can’t rely on the garbage collector to eventually do its job.