Можно ли использовать изменяемый объект в качестве ключа в словаре? - PullRequest
1 голос
/ 25 июня 2010

Скажем, у меня есть какой-то особый класс WrappedDataTable, и я хочу связать каждый WrappedDataTable ровно с одним DataTable. Кроме того, я хочу, чтобы для любого данного DataTable.

существовало не более одного WrappedDataTable.

Коллега предложил мне кешировать свой WrappedDataTable и использовать фабричный метод для доступа к нему, например:

public static class DataTableWrapper
{
    private Dictionary<DataTable, WrappedDataTable> _wrappedTables;

    static DataTableWrapper()
    {
        _wrappedTables = new Dictionary<DataTable, WrappedDataTable>();
    }

    public static WrappedDataTable Wrap(this DataTable table)
    {
        WrappedDataTable wrappedTable;
        if (!_wrappedTables.TryGetValue(table, out wrappedTable))
            _wrappedTables[table] = wrappedTable = new WrappedDataTable(table);

        return wrappedTable;
    }
}

Поначалу это показалось мне очень сомнительным, наверное, потому что я познакомился с идеей, что ключи в словаре должны быть неизменными. Но, возможно, это не обязательно так? Быстрый тест показал мне, что DataTable, кажется, поддерживает непротиворечивый хэш-код в течение многочисленных модификаций его содержимого; * Dictionary<DataTable, TValue>, следовательно, может последовательно возвращать правильное значение для ContainsKey.

Что мне интересно, так это то, что базовая версия object.GetHashCode по умолчанию будет возвращать неизменное значение для каждого отдельного объекта, или если то, что я вижу с DataTable, является просто иллюзией?

Если первое верно - object.GetHashCode работает просто отлично - похоже, совет «использовать только неизменяемые типы в качестве ключей» действительно применим только к сценариям, где:

  1. Вы хотите, чтобы равенство объектов соответствовало равенству значений, а не ссылочному равенству, и / или:
  2. У вас есть собственный тип с собственной реализацией GetHashCode, основанной на членах типа.

Кто-нибудь из мудрецов хочет пролить свет на это для меня?


ОБНОВЛЕНИЕ : Спасибо Джону Скиту за ответ на мой вопрос. В других новостях я немного покопался и подумал, что придумал IEqualityComparer<T>, который действительно обеспечивает сравнение идентичности в конце концов! Проверьте это (извините, ненавистники VB.NET, у меня только что был проект VB.NET, поэтому я написал его - перевод тривиален):

Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Public Class IdentityComparer(Of T As Class)
    Implements IEqualityComparer(Of T)

    Public Overloads Function Equals(ByVal x As T, ByVal y As T) As Boolean _
        Implements IEqualityComparer(Of T).Equals

        Return Object.ReferenceEquals(x, y)
    End Function

    Public Overloads Function GetHashCode(ByVal obj As T) As Integer _
        Implements IEqualityComparer(Of T).GetHashCode

        Return RuntimeHelpers.GetHashCode(obj)
    End Function
End Class

Посмотрите на этот пример программы:

Dim comparer As IEqualityComparer(Of String) = New IdentityComparer(Of String)

Dim x As New String("Hello there")
Dim y As New String("Hello there")

Console.WriteLine(comparer.Equals(x, y))
Console.WriteLine(comparer.GetHashCode(x))
Console.WriteLine(comparer.GetHashCode(y))

Выход:

False
37121646
45592480

Ответы [ 2 ]

2 голосов
/ 25 июня 2010

Не нужно возвращать уникальное значение. Он просто должен вернуть неизменный - и это то, что object.GetHashCode делает.

Пока DataTable не переопределяет Equals или GetHashCode, вы в основном получаете идентичность объекта как равенство - это означает, что не имеет значения, мутирован ли объект.

Лично я хотел бы увидеть реализацию IEqualityComparer<T>, которая обеспечивает равенство идентичности для любого типа, но мы не можем реализовать это сами - нет способа узнать, что 1013 * вернул бы , если бы он не был переопределен. (Java имеет такую ​​возможность в своих стандартных библиотеках, а .NET - нет. Grr.)

РЕДАКТИРОВАТЬ: Woot - с object.ReferenceEquals и RuntimeHelpers.GetHashCode(), мы можем легко реализовать IdentityEqualityComparer<T>. Ура!

0 голосов
/ 30 июня 2010

Я думаю, вы все прекрасно понимаете.Для сохранения объекта в словаре любые характеристики, которые могут повлиять на его значение хеш-функции или проверку на равенство с другими объектами, должны быть неизменными.Характеристики, которые не влияют на эти вещи, не обязательно должны быть неизменными.

...