This question already has answers here:
Closed 2 years ago.
I saw this code recently in a struct
and I was wondering what base.GetHashCode
actually does.
public override int GetHashCode()
{
var hashCode = -592410294;
hashCode = hashCode * -1521134295 + base.GetHashCode();
hashCode = hashCode * -1521134295 + m_Value.GetHashCode();
return hashCode;
}
The coreclr repo has this comment:
Action: Our algorithm for returning the hashcode is a little bit complex. We look
for the first non-static field and get it's hashcode. If the type has no
non-static fields, we return the hashcode of the type. We can't take the
hashcode of a static member because if that member is of the same type as
the original type, we'll end up in an infinite loop.
However, the code isn't there, and it looks like that's not quite what happens. Sample:
using System;
struct Foo
{
public string x;
public string y;
}
class Test
{
static void Main()
{
Foo foo = new Foo();
foo.x = "x";
foo.y = "y";
Console.WriteLine(foo.GetHashCode());
Console.WriteLine("x".GetHashCode());
Console.WriteLine("y".GetHashCode());
}
}
Output on my box:
42119818
372029398
372029397
Changing the value of y
doesn't appear to change the hash code of foo
.
However, if we make the fields int
values instead, then more than the first field affects the output.
In short: this is complex, and you probably shouldn't depend on it remaining the same across multiple versions of the runtime. Unless you're really, really confident that you don't need to use your custom struct as a key in a hash-based dictionary/set, I'd strongly recommend overriding GetHashCode
and Equals
(and implementing IEquatable<T>
to avoid boxing).
The base class of a struct is the ValueType
class, and the source code is online. They helpfully left a comment that describes how it works:
ValueType.GetHashCode:
/*=================================GetHashCode==================================
**Action: Our algorithm for returning the hashcode is a little bit complex. We look
** for the first non-static field and get it's hashcode. If the type has no
** non-static fields, we return the hashcode of the type. We can't take the
** hashcode of a static member because if that member is of the same type as
** the original type, we'll end up in an infinite loop.
**Returns: The hashcode for the type.
**Arguments: None.
**Exceptions: None.
==============================================================================*/
[System.Security.SecuritySafeCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern override int GetHashCode();