c#Multikey Dictionary," KeyNotFoundException"当用自己的可变类替换元组时

时间:2016-04-18 09:43:28

标签: c# dictionary tuples multikey

我声明了一个可变类,它可以替代元组作为字典的关键字。原因是序列化。序列化工作得很好。出现以下问题,我得到一个" 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);
        }

1 个答案:

答案 0 :(得分:3)

CAVEAT:在可变结构上实施GetHashCode是一种灾难。 Hashcodes只有一个目的,那就是促进哈希表中的存储。在散列表中用作键的项不应该变异(用于计算散列的任何属性),因为更改散列码会导致散列表无法恢复。

为了使项目在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
        };
    }
}