I recently discovered that Visual Studio 2017 can auto-generate overrides for Equals
and GetHashCode
, but I was wondering why the GetHashCode
implementation is not in an unchecked block?
I made a simple class with two public string properties Foo and Bar, and the generated GetHashCode
implementation is shown below.
public override int GetHashCode()
{
var hashCode = -504981047;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Foo);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Bar);
return hashCode;
}
I was under the impression the unchecked for GetHashCode
implementations was important because it was very likely to overflow, and we don't want any overflow exceptions because it's fine if it wraps around.
By default C# projects do not check for overflow and underflow.
Right click on the project, Select Properties
, On the Build
Tab at the bottom select Advanced...
, Check the box labeled Check for arithmetic overflow/underflow
Now the default behavior is to throw System.OverflowException
if there is ever an overflow not in an explicit unchecked
block.
If you auto-generate overrides for Equals
and GetHashCode
with overflow checking turned on for the project, then the unchecked block is there as expected
public override int GetHashCode()
{
unchecked
{
var hashCode = -504981047;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Foo);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Bar);
return hashCode;
}
}
Apparently my understanding of no checks vs unchecked vs checked was flawed. It was simple enough to write some simple tests to see overflow behavior in this fiddle .
The quick summary is this:
If running with no explicit checks
If running explicitly unchecked
If running explicitly checked
System.OverflowException
will be thrown. So... I guess the lesson from all of this is if you have some calculations that may overflow, and you care about overflow, it is very important to put that inside of a checked
block. If you have code that may overflow, and you don't care about overflow, apparently you can skip the unchecked block (unless your code will obviously overflow from a static analysis point of view).
The code from the fiddle is copied here as well for posterity.
using System;
public class Program
{
public static void Main()
{
var rand = new Random();
int test = 0;
//obscured enough that the compiler doesn't "know" that the line will produce an overflow
//Does not run explicitly as checked, so no runtime OverflowException is thrown
test = rand.Next(Int32.MaxValue-2, Int32.MaxValue) + 10;
//simple enough that the compiler "knows" that the line will produce an overflow
//Compilation error (line 16, col 10): The operation overflows at compile time in checked mode
//test = Int32.MaxValue + 1;
//Explicitly running as unchecked. Compiler allows line that is "known" to overflow.
unchecked
{
test = Int32.MaxValue + 1;
}
Console.WriteLine(test);
//Explicitly running as unchecked. Still no runtime OverflowException
unchecked
{
test = test - 10;
}
Console.WriteLine(test);
//Explicitly running as checked. System.OverflowException: Arithmetic operation resulted in an overflow.
checked
{
test = test + 10;
}
Console.WriteLine(test);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.