3

问题

我需要一种为对象字典生成键的方法。但是,我有一些要求使这有点困难。这是场景:

  1. Dictionary 是引用类型对象的列表。
  2. Dictionary 是private,在一个静态类中。
  3. 外部代码需要获取字典中特定对象的键,但不得访问字典中的对象或字典本身。
  4. 给定字典中的特定对象,键必须始终是可重新计算/可导出的。如果对象上的属性发生更改,则密钥不得更改。
  5. 相反,如果创建的新对象可以评估为equal字典中的另一个对象,则键必须不同,因为它们是两个独立的对象。
  6. 此实现必须是线程安全的。

非解决方案

解决方案 #1
所有 .Net 对象都包含一个名为 的方法.GetHashCode(),该方法返回一个整数值。您可以将其用作键。

问题
不可能。MSDN 状态:

相等的两个对象返回相等的哈希码。

这打破了req #5,我假设(但未经测试)req。#4。如果它可以满足这些规则,我很想有这样的选择。

解决方案 #2
将指向对象的指针转换为 anint并将其用作键。

问题
这打破了req的本质。#3。传递指针并将它们用作键并不安全。

解决方案#3
将指向对象的指针转换为整数散列值并使用散列作为键。

问题
尽管这不会违反任何规则,但我更愿意避免访问指针,因为这将涉及使用unsafe代码。如果必须,我不反对使用不安全的代码,但如果可能的话,我宁愿避免使用它。

结论

可能我的要求有点挑剔。必须有某种合理的方法从唯一对象派生密钥。有没有人经历过这样的场景并解决了这个困境?

4

3 回答 3

8

1 Dictionary 是 ByRef 对象的列表。

在 .NET 中,对象始终是“通过引用”的。这可能是误解的开始。引用相等是您需要/想要的。

3 外部代码需要获取字典中特定对象的键,但不得访问字典中的对象或字典本身。

这是真正的问题。没有它,对对象本身的引用就会起作用。但是该框架仍然提供了所有现成的功能:

private Dictionary<object, MyClass> _myStore;

// add an item and return a key    
public object Add(MyClass item)
{
    object key = new object();
    _myStore.Add(key, item);
    return key;
}

并满足req #4:

private Dictionary<object, MyClass> _itemForKey;    // was _myStore
private Dictionary<MyClass, object> _keyForItem;


// add an item and return a key    
public object Add(MyClass item)
{
    object key = new object();
    _itemForKey.Add(key, item);
    _keyForItem.Add(item, key);
    return key;
}

protected object DeriveKeyFromItem(MyClass item)
{
   return _keyForItem[item];
}

注意:这些示例不是线程安全的(req 6),但这是要解决的标准功能。

于 2013-11-07T19:00:43.233 回答
1

我认为您的要求归结为您希望通过引用相等性进行比较和散列。在你的字典中使用 this 作为 theIEqualityComparer<T>可以做到这一点,使用RuntimeHelpers.GetHashCodeand object.ReferenceEquals

public class ReferenceEqualityComparer<T> : IEqualityComparer<T>
{
    public bool Equals(T x, T y)
    {
        return object.ReferenceEquals(x, y);
    }
    public int GetHashCode(T obj)
    {
        return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj);
    }
}

请注意,由于指针和引用不相同,因此您的引用不应用作指针,从而降低unsafe代码不恰当地使用它的风险。

于 2013-11-07T19:03:06.173 回答
-1

如何定义一个类似的结构:

public struct BlindIdentityToken : IEquatable<BlindIdentityToken>
{
    Object o;
    public BlindIdentityToken(Object obj)
    {
        o = obj;
    }
    public override int GetHashCode()
    {
        return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(o);
    }
    public override bool Equals(object obj)
    {
        if (obj == null || obj.GetType() != typeof(BlindIdentityToken))
            return false;
        return ((BlindIdentityToken)obj).o == o;
    }
    public bool Equals(BlindIdentityToken other)
    {
        return o == other.o;
    }
}

给定一个对象的引用,一个人可以构造 a BlindIdentityToken,它将与BlindIdentityToken为同一对象构造的任何其他对象进行比较,但将与其他任何对象进行比较。因为它是一种结构类型并实现IEquatable<BlindIdentityToken>了,所以构造一个标记并在字典中查找它不需要堆分配。因为它使用它应该忽略引用被封装的对象的RuntimeHelpers.GetHashCode()任何覆盖,并且因为它是一个私有字段,所以它在某种程度上受到保护,不受外部代码偷偷引用它的影响。GetHashCodeo

于 2013-11-13T00:28:56.773 回答