This document provides guidelines for using exceptions in C# code. It discusses why exceptions should be used instead of error codes, different exception types, how to throw, catch, and handle exceptions properly according to best practices. It also covers creating custom exception classes and handling exceptions efficiently to avoid performance issues.
3. WHY HANDLE ERRORS
Prevent program crashes
Chance to fix/retry
Meaningful Message
Graceful exit
Opportunity to log error
Good error handling code helps future maintainers understand what possible error conditions may occur and
how they can be handled. Jason Roberts
4. ERROR CODES DRAWBACKS
Need to add if/switch statements every time method is called to check return codes
Errors do not bubble up the call stack
How do you deal with system errors?
Out of memory
Access violations
How do you return an error from a constructor?
Exceptions are the primary means of reporting errors in frameworks - Microsoft
5. INTRODUCING EXCEPTIONS
System.Exception
Generated with the throw statement
Different exception classes represent different errors
Exception classes can contain additional error information
Different exceptions can be handled differently
An exception is any error condition or unexpected behavior that is encountered by an executing
program Microsoft Documentation
6. WHY EXCEPTIONS?
Dont need to know all error / success codes
Dont need if / switch statements everywhere method is called
More readable
No magic numbers / constants
Exceptions can bubble up
Catch exceptions higher up / in one place
Handle system errors
Generate exceptions from constructors
7. EXCEPTION TYPES
Standard exceptions provided by the .NET Framework ( System )
. NET Runtime (CLR) - . NET Framework
OutOfMemory
StackOverflow
Exceptions provided by framework / library authors ( Third party )
JsonSerialization
Custom application exceptions ( Your code )
RulesEngine
11. THROW EXCEPTION GUIDELINE
DO specify the inner exception when wrapping exceptions.
DO use an empty throw statement (throw;).
Do not use exceptions for normal flow of control, if possible [Microsoft]
E.g. input validation
You expect input to invalid sometimes
Not an exceptional situation
Part of expected logic flow
IsValid(xxx) method(s)
DO NOT throw a System.Exception or System.ApplicationException.
DO NOT have public members that return exceptions as the return value or an `out` parameter
DO NOT throw exceptions from Exception Filter blocks as it returns false and it will be difficult to debug
12. EXCEPTION MESSAGE GUIDELINES
Describes the reason for the exception
Written for developer who going to handling the exception
Should completely describe the error, and how to correct error (where possible / applicable)
May sometimes be shown to end-user
May sometimes be logged
Correct grammar
Correct punctuation
Correct spelling
End sentences with full stop
Consider error message localization
Should not include passwords, security, or sensitive data
13. CATCH EXCEPTION
Catching Exceptions (Try, Catch, Finally)
Catching Different Exception Types
Re-throwing Exceptions
Catching and Wrapping Exceptions
Filtering Catch Blocks with Exception Filters
Global Exception Handling
14. CATCH EXCEPTION GUIDELINES
Catch only exceptions that you can handle
Catch only little code that you need
Catch exception only once per thread (DO NOT over-catch)
Use Exception Logging Libraries
CONSIDER terminating the process by calling `System.Environemt.FailFast` instead of throwing an exception if your code
encounters a situation where it is unsafe for further execution [Microsoft]
DO NOT Handle Exceptions inside loops
DO NOT hide (bury) exceptions you dont fully handle
Do not add a catch block that does nothing or just re-throws
May just be to log the error
Usually bad practice to ignore (swallow / trap) exceptions
AVOID catching System.Exception or System.SystemException except in top-level exception handlers that perform final
cleanup operations before rethrowing the exception.
15. FINALLY BLOCK GUIDELINES
AVOID explicitly throwing exceptions from inside the block
Implicitly thrown exceptions resulting from calling methods that throw are acceptable
Use for cleanup
E.g. calling Dispose()
17. CUSTOM EXCEPTIONS GUIDELINES
Naming convention: Exception
Implement standard 3 constructors
Add additional properties where needed
Never inherit from ApplicationException class
Inherit from System.Exception (or your other custom exception)
Keep the number of custom exception types a minimum
Wrap inner exception if appropriate
Interfacing with external API, DLL, service
Use existing predefined .NET exception types where applicable, e.g.
InvalidOperationException if property set/method call is not appropriate for current state
ArgumentException (or derived) for invalid parameters
Dont use custom (or existing) exceptions for normal (non exceptional) logic flow
18. EXPENSIVE EXCEPTIONS
CONSIDER performance overhead of throwing and handling exceptions
Throw rates above 100 per second are likely to noticeably impact most applications performance
Huge StackTrace
19. GUARDS
Design code to avoid exceptions
Framework designers should design APIs so users can write code that does not throw exceptions
Tester-Doer Pattern
Try-Parse Pattern
Instead of using `int Parse(string input)` use `bool TryParse(string input, out int result)`
Check connection state before trying to close it `if(cn.State != ConnectionState.Closed) { cn.Close(); }`
Consider returning null (or null object pattern) for extremely common errors
Use Check and Guard clauses
20. REFERENCES & DISCUSSION
Error Handling in C# with Exceptions by Jason Roberts
Microsoft Documentation [Exceptions]
Illustrated C# 2012 [Book]
#12: DO use an empty throw statement (throw;) when rethrowing the same exception rather than passing the exception as an argument to throw.
DO throw ArgumentException or one of its subtypes if bad arguments are passed to a member. Prefer the most derived exception type (ArgumentNullException, for example), if applicable.
DO use nameof for the paramName argument passed into argument exception types like ArgumentException, ArgumentOutOfRangeException, and ArgumentNullException that take such a parameter.
DO set the ParamName property when throwing an ArgumentException or one of the subclasses.
DO document all exceptions thrown by publicly callable members because of a violation of the member contract (rather than a system failure) and treat them as part of your contract
Exceptions that are a part of the contract should not change from one version to the next (i.e., exception type should not change, and new exceptions should not be added).
DO NOT throw a NullRefernceException, favoring ArgumentNullException instead when a value is unexpectedly null.
DO NOT have public members that return exceptions as the return value or an `out` parameter
Returning exceptions from public APIs instead of throwing them defeats many of the benefits of exception-based error reporting.
#20: X DO NOTuse exceptions for the normal flow of control, if possible.
Except for system failures and operations with potential race conditions, framework designers should design APIs so users can write code that does not throw exceptions. For example, you can provide a way to check preconditions before calling a member so users can write code that does not throw exceptions.
The member used to check preconditions of another member is often referred to as a tester, and the member that actually does the work is called a doer.
There are cases when the Tester-Doer Pattern can have an unacceptable performance overhead. In such cases, the so-called Try-Parse Pattern should be considered (seeExceptions and Performancefor more information).