On occasion, an exception thrown at a lower level in the stack will no longer make sense when caught at a higher level. For example, consider a System.IO.IOException that occurs because a system is out of disk space on the server. A client catching such an exception would not necessarily be able to understand the context of why there was even I/O activity. Similarly, consider a geographic coordinate request API that throws a System.UnauthorizedAccessException (an exception totally unrelated to the API called). In this second example, the caller has no context for understanding what the API call has to do with security. From the perspective of the code that invokes the API, these exceptions cause more confusion than they help diagnose. Instead of exposing such exceptions to the client, it might make sense to first catch the exception and then throw a different exception, such as InvalidOperationException (or even perhaps a custom exception), as a means of communicating that the system is in an invalid state. In such scenarios, be sure to set the InnerException property of the wrapping exception (generally via a constructor call such as new InvalidOperationException(string, Exception)) so that there is additional context that can be used for diagnostic purposes by someone closer to the framework that was invoked.
An important detail to remember when considering whether to wrap and rethrow an exception is the fact that the original stack trace—which provides the context of where the exception was thrown—will be replaced with the new stack trace of where the wrapping exception is thrown (assuming ExceptionDispatchInfo is not used). Fortunately, when the original exception is embedded into the wrapping exception, the original stack trace is still available.
Ultimately, the intended recipient of the exception is the programmer writing code that calls your API—possibly incorrectly. Therefore, you should provide as much information as possible to them to indicate both what the programmer did wrong and—perhaps more important—how to fix it. The exception type is a critical piece of the communication mechanism, so you must choose it wisely.
As we first discussed in a Chapter 2 Advanced Topic, C# provides special keywords for marking a code block with instructions to the runtime for what should happen if the target data type is too small to contain the assigned data. By default, if the target data type cannot contain the assigned data, the data will be truncated during assignment. For an example, see Listing 11.6 with Output 11.1.
The code in Listing 11.6 writes the value -2147483648 to the console. However, placing the code within a checked block or using the checked option when running the compiler will cause the runtime to throw an exception of type System.OverflowException. The syntax for a checked block uses the checked keyword, as shown in Listing 11.7 with Output 11.2. If the calculation involves only constants, the calculation will be checked by default.
As shown in Output 11.2, an exception is thrown if, within the checked block, an overflow assignment occurs at runtime.9 Note that the location information in Output 11.2 (Program.cs:line X) appears only in debug compilations—that is, compilations using the /Debug option of the compiler.
To change an entire project to be unchecked or checked, set the CheckForOverflowUnderflow property to false or true, respectively.
<PropertyGroup>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>
C# also supports an unchecked block that truncates the data instead of throwing an exception for assignments within the block (see Listing 11.8 with Output 11.3).
Even if the checked option is on during compilation, the unchecked keyword in the code in Listing 11.8 will prevent the runtime from throwing an exception during execution.
Equivalent checked and unchecked expressions are available for cases where statements are not allowed. For example, a field initializer may consist of an expression rather than a statement:
int number = unchecked(int.MaxValue + 1);
________________________________________