c#多键字典,用自己的可变类替换元组时出现“KeyNotFoundException”[重复]

Posted

技术标签:

【中文标题】c#多键字典,用自己的可变类替换元组时出现“KeyNotFoundException”[重复]【英文标题】:c# Multikey Dictionary, "KeyNotFoundException" when replacing Tuple by own mutable class [duplicate] 【发布时间】:2016-08-09 23:34:12 【问题描述】:

我声明了一个可变类,它可以替代元组作为字典的键。原因是序列化。序列化工作得很好。出现以下问题,我在使用此类时收到“KeyNotFoundException”,但前提是使用此类的新实例进行查找。为了更清楚地说明这一点,请参阅以下类定义:

    public class STuple<T1, T2>    
    public T1 Item1  get; set; 
    public T2 Item2  get; set; 


    public static implicit operator Tuple<T1, T2>(STuple<T1, T2> st)
    
        return Tuple.Create(st.Item1, st.Item2);
    

    public static implicit operator STuple<T1, T2>(Tuple<T1, T2> t)
    
        return new STuple<T1, T2>()
        
            Item1 = t.Item1,
            Item2 = t.Item2,
        ;
    

    public STuple()
    
    
    public STuple(T1 t1, T2 t2) : this()
    
        Item1 = t1;
        Item2 = t2;
    

这里是示例程序:

        Dictionary<Tuple<string, string>, double> TupleDic = new Dictionary<Tuple<string, string>, double>();
        TupleDic.Add(new Tuple<string, string>("Name1", "Name2"), 5);
        TupleDic.Add(new Tuple<string, string>("Name3", "Name4"), 10);

        Console.WriteLine("dict-Entry 1: 0", TupleDic[new Tuple<string, string>("Name1", "Name2")]);
        Console.WriteLine("dict-Entry 2: 0", TupleDic[new Tuple<string, string>("Name3", "Name4")]);


        Dictionary<STuple<string, string>, double> STupleDic = new Dictionary<STuple<string, string>, double>();
        STuple<string, string> STuple1 = new STuple<string, string>("Name1", "Name2");
        STuple<string, string> STuple2 = new STuple<string, string>("Name3", "Name4");
        STupleDic.Add(STuple1, 5);
        STupleDic.Add(STuple2, 10);


        //Still working
        Console.WriteLine();
        Console.WriteLine("Sdict-Entry 1: 0", STupleDic[STuple1]);
        Console.WriteLine("Sdict-Entry 2: 0", STupleDic[STuple2]);

        //Not working
        STuple<string, string> STuple3 = new STuple<string, string>("Name1", "Name2");
        STuple<string, string> STuple4 = new STuple<string, string>("Name3", "Name4");
        Console.WriteLine();
        Console.WriteLine("Sdict-Entry 1: 0", STupleDic[STuple3]);
        Console.WriteLine("Sdict-Entry 2: 0", STupleDic[STuple4]);

        Console.ReadKey();

使用普通元组的示例工作得很好,但是当我使用我自己的类 STuple 时,它​​只有在我使用与添加所用的完全相同的键(相同的实例)时才有效。我是一个初学者,是否可能因为一些混淆了值类型和引用类型而出现问题?

在我看来真的很奇怪,使用 foreach 查找仍然有效:

            Console.WriteLine();
        foreach (KeyValuePair<STuple<string, string>, double> s in STupleDic)
        
            Console.WriteLine("Sdict-Entry 1: 0", s.Value);
        

【问题讨论】:

您必须在 STuple 中覆盖 Equals 和 GetHashCode。 @Johannes :即使您接受了我的回答,我还是敦促您重新考虑在哈希表中使用可变键。散列表不会观察其键的变化,所以如果你要改变在散列表中用作键的东西,你会破坏散列表。真的很容易做到……真的很难调试。 @spender:谢谢你的评论。存储的数据量将保持相当小,因此我希望可以避免此类问题。此外,一旦我创建了这个数据库,密钥应该保持不变。然而,正如我所指出的,我是一个真正的初学者(来自机械工程,并试图为我的工作领域一起解决一些问题),所以总是很高兴提出建议。您有什么建议,什么更适合可序列化的多键字典?最好的问候。 只要你确定它们不会改变,你就没事。我很想向您的STuple 添加一个方法来冻结对象并防止进一步更改,也许是通过设置一个标志,如果您在调用 freeze 方法后尝试更改它们,则会导致设置器抛出。至少这样,如果您尝试更改冻结的STuple,您将获得早期提示。 【参考方案1】:

警告:在可变结构上实现GetHashCode 是灾难的根源。哈希码只有一个目的,那就是便于存储在哈希表中。在哈希表中用作键的项目不应发生变异(用于计算哈希的任何属性),因为更改哈希码会导致哈希表不可恢复的损坏。

为了使项目在hash-table like 集合中工作,它们必须实现相等和哈希码成员。因此,您可以(感谢 Resharper):

public class STuple<T1, T2>

    public STuple()
    
    

    public STuple(T1 t1, T2 t2)
        : this()
    
        Item1 = t1;
        Item2 = t2;
    

    public T1 Item1  get; set; 
    public T2 Item2  get; set; 

    protected bool Equals(STuple<T1, T2> other)
    
        return EqualityComparer<T1>.Default.Equals(Item1, other.Item1) &&
               EqualityComparer<T2>.Default.Equals(Item2, other.Item2);
    

    public override bool Equals(object obj)
    
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != GetType()) return false;
        return Equals((STuple<T1, T2>) obj);
    

    public override int GetHashCode()
    
        unchecked
        
            return (EqualityComparer<T1>.Default.GetHashCode(Item1)*397) ^
                   EqualityComparer<T2>.Default.GetHashCode(Item2);
        
    

    public static bool operator ==(STuple<T1, T2> left, STuple<T1, T2> right)
    
        return Equals(left, right);
    

    public static bool operator !=(STuple<T1, T2> left, STuple<T1, T2> right)
    
        return !Equals(left, right);
    


    public static implicit operator Tuple<T1, T2>(STuple<T1, T2> st)
    
        return Tuple.Create(st.Item1, st.Item2);
    

    public static implicit operator STuple<T1, T2>(Tuple<T1, T2> t)
    
        return new STuple<T1, T2>
        
            Item1 = t.Item1,
            Item2 = t.Item2
        ;
    

【讨论】:

完美运行。非常感谢。

以上是关于c#多键字典,用自己的可变类替换元组时出现“KeyNotFoundException”[重复]的主要内容,如果未能解决你的问题,请参考以下文章

python学习--day1(列表元组字典的区别)

18.07.06

字典 元组 集合

字典,元组,集合类型

Python之字典

c#中的多键字典? [复制]