2016-11-24 1 views
7
public class TestHashRace 
{ 
    private int cachedHash = 0; 
    private readonly object value; 

    public object Value 
    { 
     get { return value; } 
    } 

    public TestHashRace(object value) 
    { 
     this.value = value; 
    } 

    public override int GetHashCode() 
    { 
     if (cachedHash == 0) { 
      cachedHash = value.GetHashCode(); 
     } 
     return cachedHash; 
    } 

    //Equals isn't part of the question, but since comments request it, here we go: 
    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != GetType()) return false; 
     return Equals((TestHashRace) obj); 
    } 

    protected bool Equals(TestHashRace other) 
    { 
     return Equals(value, other.value); 
    } 
} 

Вот простой класс испытаний.Гарантированы ли мы, что кеширование хэш-кода через гонку данных будет работать правильно?

Гарантировано, что GetHashCode всегда будет возвращать то же значение? И если да, может кто-нибудь указать на какой-то справочный материал, который дает нам эту гарантию?

Мы не беспокоимся, если он вычисляет хэш-код для нашего значения более одного раза, мы просто хотим быть уверены, что возвращаемое значение всегда будет одинаковым.

Наш класс должен быть неизменным, а поле cachedHash является изменяемым. Поле не может быть неустойчивым по соображениям производительности (вся идея этого вопроса и оптимизация, которую мы задаем здесь). Значение неизменное. И он должен быть потокобезопасным.

Мы можем жить с перерасчётом потенциального хэш-кода, когда он будет равен 0 для некоторых значений. Мы не хотим использовать типы с нулевым значением или добавлять дополнительные поля по соображениям памяти (меньше памяти используется, если мы сохраняем только 1 int), поэтому для обработки проблемы hashcode должно быть одно int-поле.

+0

Непонятно, что вы пытаетесь сделать здесь. У вас нет возможности узнать, когда изменится значение «value» - все, что имеет внешнюю ссылку, потенциально может изменить его, что приведет к сохранению вашего значения кэшированного хэша. Вы хотите «тот же» хэш-код или хотите «правильный» хэш? Единственный способ, которым это может работать, - это если объект 'value' может уведомить содержащийся класс, если был изменен таким образом, который изменит его хэш (или вам нужно каким-то образом гарантировать, что' value' не будет изменено извне - сохраните клон или глубокая копия и т. д.). Должно ли это быть потокобезопасным? Слишком много вопросов здесь ... –

+0

Преодоление GetHashCode без переопределения Равенство приведет к ошибкам. Вы должны показать равную реализацию слишком –

+0

@J ... отредактированный вопрос для удовлетворения ваших требований –

ответ

6

Мы гарантируем, что GetHashCode всегда будет возвращать то же значение?

№. Гарантия распространяется только на неизменяемые value объекты с надлежащим образом реализованным способом GetHashCode. Изменяемые объекты могут изменять свой хеш-код, когда их содержимое было мутировано (что является причиной того, что изменяемые объекты не должны использоваться в качестве хеш-ключей).

Это верно, даже если TestHashRace сам неизменен, потому что вы можете сделать это:

var evil = new StringBuilder("hello"); 
var thr = new TestHashRace(evil); 
RunConcurrentCode(thr); 
evil.Append(", world!"); 

Если несколько потоков в RunConcurrentCode начать вызов thr «s GetHashCode в то же время, а затем полная на разных стороны Append, число, возвращенное с value.GetHashCode, может отличаться.

[Edit:] Значение неизменен

Тогда единственное, что требуется для гарантии проведения является то, что value «s GetHashCode правильно реализована, т.е. не использует случайные вещи и т.д.

Примечание: Поскольку нуль является законным значением для хеш-кода, ваш код может повторно называть valueGetHashCode, когда фактический код равен нулю. Один из подходов, чтобы исправить это было бы использовать обнуляемый cachedHash:

int? cachedHash; 
... 
public override int GetHashCode() { 
    return cachedHash ?? (cachedHash = value.GetHashCode()); 
} 
+0

Спасибо. Я добавил несколько изменений к вопросу. Естественно сказать, что нам гарантирована одинаковая стоимость, но откуда эта гарантия? Некоторое доказательство, ссылка или что-то было бы здорово. –

+0

@ ValentinKuzub Он исходит из неизменности объекта и предположения, что 'GetHashCode' ведет себя как чистая функция, т. Е. Возвращает то же значение для той же комбинации входных параметров. В этом случае входными параметрами являются состояние объекта.От Microsoft: [«Метод GetHashCode для объекта должен последовательно возвращать один и тот же хэш-код, если не будет изменений в состоянии объекта, которое определяет возвращаемое значение метода Equals объекта.»] (Https://msdn.microsoft .com/en-us/library/system.object.gethashcode% 28v = vs.110% 29.aspx? f = 255 & MSPPError = -2147217396) – dasblinkenlight

+0

«В этом случае входными параметрами являются состояние объекта». - но состояние нашего объекта может быть другим в нашем случае, hashcode изменяет состояние объекта. Он пытается казаться чистой функцией, но не чист. Отсюда вопрос. –

3

Нет, это не будет, потому что 0 является допустимым результатом для value.GetHashCode(). Сделайте cacheedHash nullable int и проверьте значение null вместо 0.

-1

Нет никакой гарантии, потому что вы можете пойти и реализовать класс с помощью метода , совершающего произвольные глупые вещи. Компилятор не помешает вам.

Другой вопрос: можете ли вы ожидать, что GetHashCode всегда возвращает то же значение. Ответ на этот вопрос - да, в основном. Это дизайнерское решение. Однако для большинства классов возможность использовать экземпляры в качестве ключа в словаре достаточно важна для реализации GetHashCode таким образом, чтобы значение никогда не менялось, например, не перекрывая его, или только переопределяя его, чтобы сохранить затраты на отражение.

Следует отметить, что это включает в себя StringBuilder, так что условие гонки было отмечено dasblinkenlight на самом деле не существует: в отличие от String, StringBuilder всегда будет возвращать один и тот же хэш-код.

Так почему же в основном? Ответ на это немного неудобен. Технически класс string не является неизменным. Есть некоторые злые (т. Е. Небезопасные) способы изменения содержимого строки без изменения ссылки, что, в свою очередь, приведет к разным хеш-кодам для этой же ссылки. Вы также найдете множество людей, которые используют значения Equals и GetHashCode для классов, которые страдают от одной и той же проблемы (и вам не нужно использовать небезопасный код, чтобы попасть в проблему).

Таким образом, нет гарантии, но это справедливое предположение. Просто задокументируйте это предположение, чтобы пользователи вашего кода не сталкивались с трудностями, и все должно быть в порядке.

+0

Почему downvote? Пожалуйста, оставьте комментарий, если вы считаете, что что-то не так. – Georg

Смежные вопросы