Вы столкнулись с тем, что считаете проблемой, однако, если бы вы посмотрели на их хэш-коды в одном и том же исполнении , вы обнаружите, что они не идентичны, а вместо этого полагаютсяв порядке их использования:
Console.WriteLine("{0} {1:08X}", typeof(string), typeof(string).GetHashCode());
Console.WriteLine("{0} {1:08X}", typeof(Program), typeof(Program).GetHashCode());
// System.String 02BF8098
// Program 00BB8560
Если я снова запустите ту же программу, поменяв местами их порядок:
Console.WriteLine("{0} {1:08X}", typeof(Program), typeof(Program).GetHashCode());
Console.WriteLine("{0} {1:08X}", typeof(string), typeof(string).GetHashCode());
// Program 02BF8098
// System.String 00BB8560
Это не проблема во время выполнения, так как возвращаемые значения ненарушать правила реализации Object.GetHashCode
.
Но, как вы отметили, это поведение кажется любопытным!
Я углубился в исходный код и обнаружил, что реализация Type.GetHashCode
навязана MemberInfo.GetHashCode
, который снова навязан на Object.GetHashCode
, который вызывает RuntimeHelpers.GetHashCode(this)
.
Именно в этот момент тропа остывает, однако я предполагаю, что внутренняя работа этого метода создает новое значение,сопоставляется для каждого экземпляра на основе порядка вызовов.
Я проверил эту гипотезу, выполнив один и тот же код выше с двумя экземплярами Program
(после добавления свойства для их идентификации):
var b = new Program() { Name = "B" };
var a = new Program() { Name = "A" };
Console.WriteLine("{0} {1:08X}", a.Name, a.GetHashCode());
Console.WriteLine("{0} {1:08X}", b.Name, b.GetHashCode());
// A 02BF8098
// B 00BB8560
Таким образом, для сКлассы, которые явно не переопределяют Object.GetHashCode
, экземплярам будет присвоено, казалось бы, предсказуемое хэш-значение, основанное на порядке, в котором они вызывают GetHashCode
.
Обновление: Я пошели посмотрел на то, как Rotor / Shared Source CLI обрабатывает эту ситуацию, и я узнал, что реализация по умолчанию вычисляет и сохраняет хеш-код в блоке синхронизации для экземпляра объекта, таким образом гарантируя, что хеш-код генерируется только один раз.Вычисление по умолчанию для этого хэш-кода является тривиальным и использует начальное значение для каждого потока (обертывание - мое):
// ./sscli20/clr/src/vm/threads.h(938)
// Every thread has its own generator for hash codes so that we
// won't get into a situation where two threads consistently give
// out the same hash codes.
// Choice of multiplier guarantees period of 2**32
// - see Knuth Vol 2 p16 (3.2.1.2 Theorem A).
Так что, если фактическая CLR следует этой реализации, могут возникнуть различия в значениях хеш-кодадля объектов основаны на AppDomain и Managed Thread, который создал экземпляр.