如何在不提供确切参考的情况下获得该值

本文关键字:情况下 参考 | 更新日期: 2023-09-27 18:20:48

更准确地说,我有这样的东西:

Dictionary<KeyValuePair<MyState, MyAction>, float> Dict = 
     new Dictionary<KeyValuePair<MyState, MyAction>, float>();

稍后,我有一个自定义的KeyValuePair对象;

KeyValuePair<MyState, MyAction> kvp = 
    new KeyValuePair<MyState, MyAction>(new MyState(...), new MyAction(...));

我的问题是:如果kvp状态和操作与Dict中的一对具有完全相同的值(在Dict中存在一个KeyValuePair,其MyState与kvp-MyAction具有完全相同,并且来自Dict的MyAction与kvp中的MyAction具有绝对相同的值)。唯一的区别是引用不同。

长话短说,有两个KeyValuePairs对象,它们都有相同的值(不同的引用),我如何从Dict中获得float value,而不必迭代整个字典来手动比较每个key.key和key.value,看看键是否真的相同:

foreach(var StAct in Dict)
    if(StAct.Key.Key.Equals(kvp.Key) && 
        StAct.Key.Value.Equals(kvp.value))
  //where StAct.Key.Key is the MyState object and StAct.Key.Value is the MyAction object
    {
        MessageBox.Show(StAct.Value + "");
        break;
    }

如何在不提供确切参考的情况下获得该值

您需要确保在作为Dictionary键的MyState对象内部,您已经正确地重写了Equals()和GetHashCode()方法。

正如您所指出的,类的默认行为是检查引用相等性,因此如果您想要一些不同的行为,则必须自己提供。

public class MyState
{
    public override bool Equals(object obj)
    {
        // your equality implementation goes here
    }
    public override int GetHashCode()
    {
        // your hashcode implementation goes here
    }
}

创建一个行为良好的GetHashCode()方法并不一定是微不足道的,但你可以在下面的答案中找到一些关于如何做到这一点的好建议:https://stackoverflow.com/a/371348/5438433

一旦你做到了,你可以简单地写:

if(dict.ContainsKey(kvp.Key)) {.....}

您需要在KeyValuePair<>-类上实现GetHashCode()Equals()。这些是Dictionary<>用来查找密钥的方法。

规则是,如果覆盖Equals(),则必须覆盖GetHashCode(),因为必须确保两个"相等"的对象始终生成相同的哈希代码。这是Dictionary<>正常工作所必需的。

Dictionary<>首先通过搜索其哈希代码来寻找正确的密钥,然后才使用Equals()来确保它真的找到了正确的密钥。

但是,对于不相等的对象,可以具有相同的哈希代码。例如,通过始终返回1来实现GetHashCode()是完全合法的,因为它满足相等对象具有相同哈希码的规则。然而,这会使Dictionary<>的操作非常缓慢——基本上,Dictionary<>现在必须搜索其所有条目才能找到正确的键,就像它是一个简单的列表一样。

对于KeyValuePair<>,您可能希望两个KeyValuePair<>对象相等,前提是它们的键和值依次相等。你可以这样实现:

public class KeyValuePair<K, V>
{
    private K m_key;
    private V m_value;
    // [...] existing implementation left out
    public override bool Equals(object obj)
    {
        KeyValuePair<K, V> other = obj as KeyValuePair<K, V>;
        if(other == null)
            return false;
        return object.Equals(this.m_key, other.m_key) && object.Equals(this.m_value, other.m_value);
    }
    public override int GetHashCode()
    {
        int hashCode = 0;
        if(m_key != null)
            hashCode += m_key.GetHashCode();
        if(m_value != null)
            hashCode = hashCode * 31 + m_value.GetHashCode();
        return hashCode;
    }
}

请注意,这需要K和V还以对您有意义的方式实现Equals()GetHashCode()

发布详细示例:

public class Foo
{
    public int Id { get; set; }
    public string  Name { get; set; }
    public override bool Equals(object obj)
    {   
        return this.Id==((Foo)obj).Id
            && this.Name==((Foo)obj).Name;
    }
    public override int GetHashCode()
    {
        return Id.GetHashCode() + Name.ToLower().GetHashCode();
    }
}
public class Bar
{
    public int SomeOtherId { get; set; }
    public string SomeOtherName { get; set; }
    public override bool Equals(object obj)
    {
        return this.SomeOtherId == ((Bar)obj).SomeOtherId
            && this.SomeOtherName == ((Bar)obj).SomeOtherName;
    }
    public override int GetHashCode()
    {
        return SomeOtherId.GetHashCode() + SomeOtherName.ToLower().GetHashCode();
    }
}
//Usage
var dict = new Dictionary<KeyValuePair<Foo, Bar>, float>();
dict.Add(new KeyValuePair<Foo, Bar>(new Foo { Id = 1, Name = "Foo" }, new Bar {SomeOtherId = 1, SomeOtherName = "Bar"}), 10);
Console.WriteLine(dict[new KeyValuePair<Foo, Bar>(new Foo { Id = 1, Name = "Foo" }, new Bar {SomeOtherId = 1, SomeOtherName = "Bar"})]);

答案与帖子的标题一致。然而,如果字典中有相同的键(尽管引用不同),这仍然不成立,因为当你尝试添加这样的键(在你应用了Equals&GetHashCode之后),它不允许你插入这些相同的键。