Итак, ответ JaredPar не плохой , но может быть лучше несколькими способами. Прежде всего, на странице IEqualityComparer написано: «Мы рекомендуем вам наследовать от класса EqualityComparer вместо реализации интерфейса IEqualityComparer».
Во-вторых, реализация GetHashCode должна быть быстрой . Он используется для быстрого удаления явно разных объектов, что, очевидно, было бы пустой тратой времени на запуск Equals. Так что GetHashCode должен быть намного быстрее, чем на самом деле работает Equals.
В-третьих, возвращение суммы байтового массива, как это сделал JaredPar, может привести к коллизиям - если байты расположены в другом порядке, или относительные различия компенсируют друг друга и т. Д.
Поэтому я бы порекомендовал решение, подобное этому:
public class ByteArrayComparer : EqualityComparer<byte[]>
{
public override bool Equals(byte[] first, byte[] second)
{
if (first == null || second == null) {
// null == null returns true.
// non-null == null returns false.
return first == second;
}
if (ReferenceEquals(first, second)) {
return true;
}
if (first.Length != second.Length) {
return false;
}
// Linq extension method is based on IEnumerable, must evaluate every item.
return first.SequenceEqual(second);
}
public override int GetHashCode(byte[] obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
// quick and dirty, instantly identifies obviously different
// arrays as being different
return obj.Length;
}
}
Выше, возвращая obj.Length, действительно быстр и грязен, но также склонен к возвращению большого количества столкновений. Я думаю, что мы можем сделать лучше.
Если вы собираетесь исследовать все байты, то что-то вроде этого менее подвержено столкновениям, чем простая сумма байтов, как в ответе JaredPar. Но опять же, это проверяет все элементы, поэтому он не будет работать лучше, чем на самом деле работает Equals. Вы также можете просто вернуть 0 безоговорочно и всегда принудительно использовать Equals.
Я подчеркиваю: это лучше, чем возвращать сумму, как в ответе JaredPar. И всегда возвращать 0 лучше, чем это. И возвращать obj.Length лучше, чем возвращать 0.
// This is not recommended. Performance is too horrible.
public override int GetHashCode(byte[] obj)
{
// Inspired by fletcher checksum. Not fletcher.
if (obj == null) {
throw new ArgumentNullException("obj");
}
int sum = 0;
int sumOfSum = 0;
foreach (var val in obj) {
sum += val; // by default, addition is unchecked. does not throw OverflowException.
sumOfSum += sum;
}
return sum ^ sumOfSum;
}
Если вам известно, что байтовые массивы [], которые вы используете в качестве ключа, сами по себе являются криптографическими хешами, то вы можете использовать это предположение в своих интересах и просто вернуть первые 4 байта, преобразованные в int
. Это, вероятно, тоже хорошо работает для байтовых массивов общего назначения:
// This implementation works great if you assume the byte[] arrays
// are themselves cryptographic hashes. It probably works alright too,
// for general-purpose byte arrays.
public override int GetHashCode(byte[] obj)
{
if (obj == null) {
throw new ArgumentNullException("obj");
}
if (obj.Length >= 4) {
return BitConverter.ToInt32(obj, 0);
}
// Length occupies at most 2 bits. Might as well store them in the high order byte
int value = obj.Length;
foreach (var b in obj) {
value <<= 8;
value += b;
}
return value;
}