8

Quick example:

using System;
using System.Globalization;

var d1 = new DateTime(2023, 2, 28, 0, 0, 0, DateTimeKind.Unspecified);
var d2 = new DateTime(2023, 2, 28, 0, 0, 0, DateTimeKind.Local);

var foo1 = new Foo { d = d1 };
var foo2 = new Foo { d = d2 };

Console.WriteLine($"d1.Equals(d2): {d1.Equals(d2)}");
Console.WriteLine($"d1.GetHashCode() == d2.GetHashCode(): {d1.GetHashCode() == d2.GetHashCode()}");
Console.WriteLine($"foo1.Equals(foo2): {foo1.Equals(foo2)}");
Console.WriteLine($"foo1.GetHashCode() == foo2.GetHashCode(): {foo1.GetHashCode() == foo2.GetHashCode()}");

struct Foo {
    public DateTime d;
}

Prints:

d1.Equals(d2): True
d1.GetHashCode() == d2.GetHashCode(): True
foo1.Equals(foo2): True
foo1.GetHashCode() == foo2.GetHashCode(): False

(playground, net7)

DateTime intentionally ignores Kind in its Equals and GetHashCode methods. However, it seems that it is not ignored in the GetHashCode methods of a simple struct which contains a DateTime.

My guess is it might be related to this optimization which uses memcmp when all fields are 8 bytes wide (discussed for example here).

However, is it possible that it is used only for GetHashCode and not for Equals? Is it "only" an issue with DateTime or is it more general, e.g. this optimization doesn't check for overriden Equals/GetHashCode?

TylerH
  • 20,799
  • 66
  • 75
  • 101
synek317
  • 749
  • 2
  • 7
  • 22
  • 1
    My *guess* is that `ValueType` *does* normally check for overridden Equals/GetHashCode, but has an (incorrect) exception for DateTime that leads it to believe it can use just the bit pattern. Would need to test with another custom struct... Fundamentally though, it's basically always a good idea to override Equals/GetHashCode in structs anyway (and implement `IEquatable`). – Jon Skeet Mar 01 '23 at 10:10
  • 2
    I reported this bug: https://github.com/dotnet/runtime/issues/82825 – Magnetron Mar 01 '23 at 11:44

0 Answers0