Как использовать идентификатор объекта в качестве ключа для словаря <K, V>
Можно ли использовать объект как ключ для Dictonary<object, ...>
таким образом, чтобы Словарь рассматривал объекты как равные, только если они идентичны?
Например, в приведенном ниже коде я хочу, чтобы строка 2 вернула 11 вместо 12:
Dictionary<object, int> dict = new Dictionary<object, int>();
object a = new Uri("http://www.google.com");
object b = new Uri("http://www.google.com");
dict[a] = 11;
dict[b] = 12;
Console.WriteLine(a == b); // Line 1. Returns False, because a and b are different objects.
Console.WriteLine(dict[a]); // Line 2. Returns 12
Console.WriteLine(dict[b]); // Line 3. Returns 12
Текущая реализация словаря использует object.Equals()
и object.GetHashCode()
на клавишах; но я ищу другой словарь, который использует объект identity как ключ (вместо значения объекта). Есть ли такой словарь в .NET или мне нужно его реализовать с нуля?
Ответы
Ответ 1
Вам не нужно создавать свой собственный словарь - вам нужно создать собственную реализацию IEqualityComparer<T>
, которая использует идентификацию для хэширования и равенства. Я не думаю, что в структуре существует такая вещь, но ее легко создать из-за RuntimeHelpers.GetHashCode
.
public sealed class IdentityEqualityComparer<T> : IEqualityComparer<T>
where T : class
{
public int GetHashCode(T value)
{
return RuntimeHelpers.GetHashCode(value);
}
public bool Equals(T left, T right)
{
return left == right; // Reference identity comparison
}
}
Я ограничил T
как ссылочный тип, чтобы в итоге вы оказались в словаре; если вы использовали это для типов значений, вы могли бы получить некоторые нечетные результаты. (Я не знаю, как это работает, я подозреваю, что это не так.)
С этим на месте остальное легко. Например:
Dictionary<string, int> identityDictionary =
new Dictionary<string, int>(new IdentityEqualityComparer<string>());
Ответ 2
Конечно, другие ответы полностью верны, но я написал свою собственную версию, чтобы удовлетворить мои потребности:
/// <summary>
/// An equality comparer that compares objects for reference equality.
/// </summary>
/// <typeparam name="T">The type of objects to compare.</typeparam>
public sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T>
where T : class
{
#region Predefined
private static readonly ReferenceEqualityComparer<T> instance
= new ReferenceEqualityComparer<T>();
/// <summary>
/// Gets the default instance of the
/// <see cref="ReferenceEqualityComparer{T}"/> class.
/// </summary>
/// <value>A <see cref="ReferenceEqualityComparer<T>"/> instance.</value>
public static ReferenceEqualityComparer<T> Instance
{
get { return instance; }
}
#endregion
/// <inheritdoc />
public bool Equals(T left, T right)
{
return Object.ReferenceEquals(left, right);
}
/// <inheritdoc />
public int GetHashCode(T value)
{
return RuntimeHelpers.GetHashCode(value);
}
}
Обоснование конструкции:
- Класс
sealed
.
Если класс не предназначен для расширения, я собираюсь избежать всех этих расходов, запечатывая его.
- Eric Lippert
Я знаю много людей (включая меня), которые считают, что классы действительно должны быть запечатаны по умолчанию.
- Jon Skeet
- Существует
Instance
статическое свойство только для чтения, чтобы открыть один экземпляр этого класса.
- Он использует
Object.ReferenceEquals()
вместо ==
, потому что ReferenceEquals
более явный.
- Он использует
RuntimeHelpers.GetHashCode()
, потому что я не хочу использовать возможно переопределенный GetHashCode
объекта, который может не соответствовать поведению ReferenceEquals
. Это также позволяет избежать нулевой проверки.
- У него есть документация.
Ответ 3
Используйте свой собственный сопоставитель сравнений
public class ObjectIdentityEqualityComparer : IEqualityComparer<object>
{
public int GetHashCode(object o)
{
return o.GetHashCode();
}
public bool Equals(object o1, object o2)
{
return object.ReferenceEquals(o1, o2);
}
}
Обратите внимание, что GetHashCode
можно переопределить, но критическая проверка выполняется с помощью Equals
.
Ответ 4
Используйте Dictionary
с помощью