2016-04-18 1 views
0

タプルの代用として、辞書のキーとして機能する可変クラスを宣言しました。理由はシリアライゼーションです。シリアライゼーションは正常に動作します。次の問題が発生し、このクラスを使用しているときに "KeyNotFoundException"が発生しますが、このクラスの新しいインスタンスがルックアップに使用されている場合に限ります。これをより明確にするために、次のクラス定義を参照してください。c#Multikey Dictionary、タプルを独自の可変クラスで置き換えるときの "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(); 

通常のタプルを使用した例はうまく動作しますが、私は自分のクラスを使用する場合私は、追加のために使用されるのと同じ同じキー(同じインスタンス)を使用する場合、それだけで動作します。私は完全な初心者です。値型と参照型のミックスアップのために問題が発生する可能性はありますか?私の意見では

本当に奇妙な、foreachの持つルックアップはまだ動作します:

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

STupleでEqualsとGetHashCodeをオーバーライドする必要があります。 – Ralf

+0

@Johannes:あなたが私の答えを受け入れたとしても、ハッシュテーブルで可変キーを使って再考するようお願いします。ハッシュテーブルは変更のキーを見ないので、ハッシュテーブルでキーとして使用されているものを変更すると、ハッシュテーブルが破損します。本当に簡単です...本当に本当にデバッグするのは難しいです。 – spender

+0

@spender:ありがとうございます。保存されるデータの量はかなり小さいので、私はこの種の問題を避けることができます。さらに、このデータベースを作成すると、キーは変更されずに残るはずです。しかし、私が指摘したように、私は本当の初心者です(機械工学から来て、自分の仕事のために何かを解決しようとしています)。シリアライズ可能なマルチキー辞書に適したものはありますか?宜しくお願いします。 – Johannes

答えて

3

CAVEAT:変更可能な構造にGetHashCodeの実装は、災害のためのレシピです。ハッシュコードには目的が1つしかありません。これは、ハッシュテーブルでの保存を容易にするためです。ハッシュテーブルのキーとして使用されるアイテムは、ハッシュコードの変更がハッシュテーブルへの回復不可能な破損を引き起こすため、変更しないでください(ハッシュを計算するために使用されるプロパティ)。

アイテムが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 
     }; 
    } 
} 
+0

完全に動作します。多くのThxs。 – Johannes

関連する問題