unchecked -keyword in C#

Jere_Sumell picture Jere_Sumell · Sep 11, 2014 · Viewed 15.1k times · Source

Maybe I'm in the basics, but I'm still studying this C# thing at school. I understand that if I add 1 to max valued Integer, which one is 32 bit, the result will be negative. I read that C# offers checked and unchecked keywords for handling overflows. Checked keyword is something, I've found useful, but how about unchecked keyword? I really can't find not much useful use for unchecked -keyworded block. Is there any? How does the next two approaches differs from each others?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Practice_6
{
    class Program
    {
        static void Main(string[] args)
        {
            int value = Int32.MaxValue;
            value++;
            //Approach 1 to make a decision
            if (value > Int32.MaxValue) {
                //Do something
            }
            value = Int32.MaxValue;
            //Approach 2 to make a decision
            unchecked {
                value++;
                //Do something
            }
            //What's the difference between these two approaches to handle overflow?
    }
}

Answer

Eric Lippert picture Eric Lippert · Sep 18, 2014

UPDATE: This question was the subject of my blog in April 2015; thanks for the interesting question!


Your first question is:

The checked keyword is useful, but how about the unchecked keyword? I really can't find a use for it. Is there any?

The C# design team is not in the habit of adding features that have no use to the language. (With the notable exception of the unary plus operator, the world's most useless operator.)

The unchecked keyword has two main use cases.

First, constant integer arithmetic is always checked by default. This can be irritating. Suppose for example you have some interop code and you wish to create a constant for the HRESULT E_FAIL:

const int E_FAIL = 0x80004005;

That's an error because that number is too big to fit into an int. But you might not want to use a uint. You might think well I'll just say

const int E_FAIL = (int)0x80004005;

But that is also illegal because constant arithmetic including conversions is always checked by default.

What you have to do is turn off checked constant arithmetic via

const int E_FAIL = unchecked((int)0x80004005);

Second, the default arithmetic for non-constant integer math in C# is unchecked, because it is faster, and because that is what many other similar languages do. However C# allows you to change the default to checked arithmetic for non-constant integer math via a compiler flag. If you've done so, and you need to turn it back off again on a temporary basis, then you have to use the unchecked block or expression syntax.

A third use case is to use the unchecked block as a form of self-documenting code, to say "I am aware that the operation I'm doing here might overflow, and that's fine with me." For example, I'll often write something like:

int GetHashCode()
{
    unchecked 
    {
        int fooCode = this.foo == null ? 0 : this.foo.GetHashCode();
        int barCode = this.bar == null ? 0 : this.bar.GetHashCode();
        return fooCode + 17 * barCode;
    }
}

The "unchecked" emphasizes to the reader that we fully expect that multiplying and adding hash codes could overflow, and that this is OK.

Your second question is:

What's the difference between these two approaches to handle overflow?

Good question.

If in your checked context you mean: I expect arithmetic to always be within bounds; if it is not then my program has a serious bug and must be terminated before it does more harm to the world then you should not do any overflow checking at all, because there are no overflows. Any exception should terminate the program; the checked context is simply making the runtime do work to verify the correctness of your code.

If in your checked context you mean I require arithmetic to be within bounds but I obtained this data from an untrustworthy source and I am too lazy to do range checking on it then you should catch the exception. However, the better practice would be to do range checking and give a more meaningful error if the data is out of range, than to have the runtime notice the problem and throw an overflow exception. I would strongly recommend range checking rather than catching an exception; don't be lazy.