Ответ 1
Это намного сложнее, чем кажется на первый взгляд. Для начала дайте значение key2 совершенно другой строке. Обратите внимание, что хеш-код остается тем же:
var k1 = new TheKey(17, "abc");
var k2 = new TheKey(17, "def");
System.Diagnostics.Debug.Assert(k1.GetHashCode() == k2.GetHashCode());
Что вполне справедливо, единственным требованием для хэш-кода является то, что одно и то же значение создает один и тот же хэш-код. Разным значениям не нужно создавать разные хэш-коды. Это физически невозможно, так как хэш-код .NET может представлять только 4 миллиарда различных значений.
Вычисление хеш-кода для структуры - сложный бизнес. Первое, что делает CLR, это проверить, содержит ли структура какие-либо ссылки ссылочного типа или имеет пробелы между полями. Ссылка требует специального лечения, потому что эталонное значение является случайным. Это указатель, значение которого изменяется, когда сборщик мусора сжимает кучу. Разрывы в структуре структуры создаются из-за выравнивания. Структура с байтом и int имеет 3-байтовый промежуток между двумя полями.
Если это не так, то все биты в структурном значении значительны. CLR быстро вычисляет хеш путем сверки битов, 32 за раз. Это "хороший" хеш, все поля в структуре участвуют в хеш-коде.
Если структура имеет поля ссылочного типа или имеет пробелы, необходим другой подход. CLR выполняет итерацию полей структуры и ищет поиск, который может генерировать хэш. Используемым является поле типа значения или ссылка на объект, которая не является нулевой. Как только он находит один, он принимает хэш этого поля, xors его с указателем таблицы методов и завершает работу.
Иными словами, только одно поле в структуре участвует в вычислении хэш-кода. В вашем случае используется только поле id. Вот почему значение члена строки не имеет значения.
Это неясный факт, который, очевидно, важно знать, если вы когда-нибудь оставите его в CLR для генерации хеш-кодов для структуры. Безусловно, лучше всего просто не делать этого. Если вам нужно, то обязательно закажите поля в структуре, чтобы первое поле выдало вам лучший хеш-код. В вашем случае просто замените поля id и Name.
Еще один интересный лакомый кусочек, "хороший" код вычисления хэша имеет ошибку. Он будет использовать быстрый алгоритм, если структура содержит System.Decimal. Проблема в том, что биты десятичного числа не являются репрезентативными для его числового значения. Попробуйте следующее:
struct Test { public decimal value; }
static void Main() {
var t1 = new Test() { value = 1.0m };
var t2 = new Test() { value = 1.00m };
if (t1.GetHashCode() != t2.GetHashCode())
Console.WriteLine("gack!");
}