为对象派生唯一字典键的最佳方法是什么?

时间:2013-11-07 18:53:48

标签: c# .net dictionary

问题

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

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

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

    问题
    不可能。 MSDN国家:

      

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

    这打破了第5号请求,我假设(但没有测试)req。 #4。如果符合这些规则我会喜欢这样的选择。

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

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

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

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

    结论

    也许我的要求有点挑剔。必须有一些合理的方法从唯一对象派生密钥。有没有人经历过这种情况并解决了这个难题?

3 个答案:

答案 0 :(得分:8)

  

1字典是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;
}

满足第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];
}

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

答案 1 :(得分:1)

我认为您的要求归结为您希望通过引用相等性进行比较和哈希。将此作为词典中的IEqualityComparer<T>使用RuntimeHelpers.GetHashCodeobject.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);
    }
}

请注意,由于指针和引用aren't the same,您的引用不应该用作指针,从而降低了unsafe代码使用不当的风险。

答案 2 :(得分:-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;
    }
}

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