- Clean Code in C#
- Jason Alls
- 898字
- 2025-04-04 12:56:15
Checked and unchecked exceptions
In unchecked mode, an arithmetic overflow is ignored. In this situation, the high-order bits that cannot be assigned to the destination type are discarded from the result.
By default, C# operates in the unchecked context while performing non-constant expressions at runtime. But compile-time constant expressions are always checked by default. When an arithmetic overflow is encountered in checked mode, an OverflowException is raised. One reason why unchecked exceptions are used is to increase performance. Checked exceptions can decrease the performance of methods by a small amount.
The rule of thumb is to make sure that you perform arithmetic operations in the checked context. Any arithmetic overflow exceptions will be picked up as compile-time errors, and you can then fix them before you release your code. That is much better than releasing your code and then having to fix customer runtime errors.
Running code in unchecked mode is dangerous as you are making assumptions about the code. Assumptions are not facts and they can lead to exceptions being raised at runtime. Runtime exceptions lead to poor customer satisfaction and can produce serious follow-on exceptions that negatively impact a customer in some way.
Allowing an application to continue running that has experienced an overflow exception is very dangerous from a business perspective. The reason for this is that data can end up in a non-reversible invalid state. If the data is critical customer data, then this can be considerably costly to the business, and you don't want that on your shoulders.
Consider the following code. This code demonstrates how bad an unchecked overflow can be in the world of customer banking:
private static void UncheckedBankAccountException()
{
var currentBalance = int.MaxValue;
Console.WriteLine($"Current Balance: {currentBalance}");
currentBalance = unchecked(currentBalance + 1);
Console.WriteLine($"Current Balance + 1 = {currentBalance}");
Console.ReadKey();
}
Imagine the horror on this customer's face when they see that adding £1 to their bank balance of £2,147,483,647 causes them to be in debt by -£2,147,483,648!

Now, it's time to demonstrate checked and unchecked exceptions with some code examples. First, start a new console application and declare some variables:
static byte y, z;
The preceding code declares two bytes that we will use in our arithmetic code examples. Now, add the CheckedAdd() method. This method will raise a checked OverflowException if an arithmetic overflow is encountered when adding two numbers that result in a number that is too big to be stored as a byte:
private static void CheckedAdd()
{
try
{
Console.WriteLine("### Checked Add ###");
Console.WriteLine($"x = {y} + {z}");
Console.WriteLine($"x = {checked((byte)(y + z))}");
}
catch (OverflowException oex)
{
Console.WriteLine($"CheckedAdd: {oex.Message}");
}
}
Then, write the CheckedMultiplication() method. Again, a checked OverflowException will be raised if an arithmetic overflow is detected during the multiplication, which results in a number that is larger than a byte:
private static void CheckedMultiplication()
{
try
{
Console.WriteLine("### Checked Multiplication ###");
Console.WriteLine($"x = {y} x {z}");
Console.WriteLine($"x = {checked((byte)(y * z))}");
}
catch (OverflowException oex)
{
Console.WriteLine($"CheckedMultiplication: {oex.Message}");
}
}
Next, we add the UncheckedAdd() method. This method will ignore any overflow that happens as a result of an addition, and so an OverflowException will not be raised. The result of this overflow will be stored as a byte, but the value will be incorrect:
private static void UncheckedAdd()
{
try
{
Console.WriteLine("### Unchecked Add ###");
Console.WriteLine($"x = {y} + {z}");
Console.WriteLine($"x = {unchecked((byte)(y + z))}");
}
catch (OverflowException oex)
{
Console.WriteLine($"CheckedAdd: {oex.Message}");
}
}
And now, we add the UncheckedMultiplication() method. This method will not throw an OverflowException when an overflow is encountered as the result of this multiplication. The exception will simply be ignored. This will result in an incorrect number being stored as a byte:
private static void UncheckedMultiplication()
{
try
{
Console.WriteLine("### Unchecked Multiplication ###");
Console.WriteLine($"x = {y} x {z}");
Console.WriteLine($"x = {unchecked((byte)(y * z))}");
}
catch (OverflowException oex)
{
Console.WriteLine($"CheckedMultiplication: {oex.Message}");
}
}
Finally, it is time to modify our Main(string[] args) method so that we can initialize the variables and execute the methods. Here, we add the maximum value for a byte to the y variable and 2 to the z variable. Then, we run the CheckedAdd() and CheckedMultiplication() methods, which will both generate OverflowException(). This is thrown because the y variable contains the maximum value for a byte.
So, by adding or multiplying by 2, you are exceeding the address space needed to store the variable. Next, we will run the UncheckedAdd() and UncheckedMultiplication() methods. Both these methods ignore overflow exceptions, assign the result to the x variable, and disregard any bits that overflow. Finally, we print a message to the screen and then exit when the user presses any key:
static void Main(string[] args)
{
y = byte.MaxValue;
z = 2;
CheckedAdd();
CheckedMultiplication();
UncheckedAdd();
UncheckedMultiplication();
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
When we run the preceding code, we end up with the following output:

As you can see, when we use checked exceptions, exceptions are raised when OverflowException is encountered. But when we use unchecked exceptions, no exception is raised.
It is apparent from the preceding screenshot that problems can arise from unexpected values and that certain behaviors can arise from using unchecked exceptions. Therefore, the rule of thumb when performing arithmetic operations must be to always use checked exceptions.
Now, let's move on and look at a very common exception that is encountered frequently by programmers, known as NullPointerException.