在C#中的多键字典?

我知道BCL中没有一个,但是谁能指出我是一个好的开源项目?

按我的意思是2个键。 😉

在他的回答中,我已经使用过元组作为杰森了。 不过,我build议你简单地将一个元组定义为一个结构:

public struct Tuple<T1, T2> { public readonly T1 Item1; public readonly T2 Item2; public Tuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2;} } public static class Tuple { // for type-inference goodness. public static Tuple<T1,T2> Create<T1,T2>(T1 item1, T2 item2) { return new Tuple<T1,T2>(item1, item2); } } 

你得到不变性, .GetHashcode.Equals免费,这(当你在等待C#4.0)是很好'简单…

但是有一个警告 :默认的GetHashcode实现(有时候) 只考虑第一个字段,所以一定要让第一个字段最有区别或者自己实现GetHashcode (比如使用FieldwiseHasher.Hash(this) ),否则你可能会运行进入可扩展性问题。

而且,你可以避免使用复杂的空值(如果你真的想要空值,只需要使你的Tuple<>空)。 稍微偏离主题,我是唯一一个在框架级缺乏对非空引用的支持方面恼怒的人吗? 我在大型项目上工作,偶尔会出现一个null在某个地方实际上不应该的地方 – 嘿presto,你会得到一个nullreferenceexception – 但有一个堆栈跟踪,指向你的引用的第一个用法,而不是实际错误的代码。

当然,.NET 4.0现在已经很老了, 大部分的使用都可以使用.NET 4.0的元组。

编辑:解决可怜的GetHashCode实现,.NET提供的结构我写了ValueUtils ,它也允许你使用你的多字段键的实名; 这意味着你可能会写如下的东西:

 sealed class MyValueObject : ValueObject<MyValueObject> { public DayOfWeek day; public string NamedPart; //properties work fine too } 

…希望能够更容易地为具有值语义的数据赋予人类可读的名称,至less在未来的C#版本中实现具有命名成员的适当元组时 ; 希望有体面的hashcode ;-)。

我使用一个Tuple作为Dictionary的键。

 public class Tuple<T1, T2> { public T1 Item1 { get; private set; } public T2 Item2 { get; private set; } // implementation details } 

确保覆盖EqualsGetHashCode并根据需要定义operator!=operator== 。 您可以展开Tuple以根据需要容纳更多项目。 .NET 4.0将包含一个内置的Tuple

元组将在.Net 4.0中,直到那时,你也可以使用一个

  Dictionary<key1, Dictionary<key2, TypeObject>> 

或者,创build一个自定义集合类来表示这…

  public class TwoKeyDictionary<K1, K2, T>: Dictionary<K1, Dictionary<K2, T>> { } 

或者,用三个键…

 public class ThreeKeyDictionary<K1, K2, K3, T> : Dictionary<K1, Dictionary<K2, Dictionary<K3, T>>> { } 

这里有很多很好的解决scheme,这里我缺less的是基于Tupletypes构build的实现,所以我自己写了一个。

既然它只是从Dictionary<Tuple<T1,T2>, T>inheritanceDictionary<Tuple<T1,T2>, T>你可以总是使用两种方式。

 var dict = new Dictionary<int, int, Row>(); var row = new Row(); dict.Add(1, 2, row); dict.Add(Tuple.Create(1, 2, row)); dict.Add(new Tuple<int, int>(1, 2)); 

这里是代码。

 public class Dictionary<TKey1,TKey2,TValue> : Dictionary<Tuple<TKey1, TKey2>, TValue>, IDictionary<Tuple<TKey1, TKey2>, TValue> { public TValue this[TKey1 key1, TKey2 key2] { get { return base[Tuple.Create(key1, key2)]; } set { base[Tuple.Create(key1, key2)] = value; } } public void Add(TKey1 key1, TKey2 key2, TValue value) { base.Add(Tuple.Create(key1, key2), value); } public bool ContainsKey(TKey1 key1, TKey2 key2) { return base.ContainsKey(Tuple.Create(key1, key2)); } } 

请注意,这个实现依赖于Tuple.Equals()实现本身:

http://msdn.microsoft.com/en-us/library/dd270346(v=vs.110).aspx

在以下情况下,obj参数被认为等于当前实例:

  • 它是一个Tuple对象。
  • 它的两个组件与当前实例的types相同。
  • 它的两个组件与当前实例的组件相同。 相等由每个组件的默认对象相等比较器决定。

我写了,并成功地使用了这个。

 public class MultiKeyDictionary<K1, K2, V> : Dictionary<K1, Dictionary<K2, V>> { public V this[K1 key1, K2 key2] { get { if (!ContainsKey(key1) || !this[key1].ContainsKey(key2)) throw new ArgumentOutOfRangeException(); return base[key1][key2]; } set { if (!ContainsKey(key1)) this[key1] = new Dictionary<K2, V>(); this[key1][key2] = value; } } public void Add(K1 key1, K2 key2, V value) { if (!ContainsKey(key1)) this[key1] = new Dictionary<K2, V>(); this[key1][key2] = value; } public bool ContainsKey(K1 key1, K2 key2) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2); } public new IEnumerable<V> Values { get { return from baseDict in base.Values from baseKey in baseDict.Keys select baseDict[baseKey]; } } } public class MultiKeyDictionary<K1, K2, K3, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, V>> { public V this[K1 key1, K2 key2, K3 key3] { get { return ContainsKey(key1) ? this[key1][key2, key3] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, V>(); this[key1][key2, key3] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3); } } public class MultiKeyDictionary<K1, K2, K3, K4, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, V>(); this[key1][key2, key3, key4] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, V>(); this[key1][key2, key3, key4, key5] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, V>(); this[key1][key2, key3, key4, key5, key6] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>(); this[key1][key2, key3, key4, key5, key6, key7] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>(); this[key1][key2, key3, key4, key5, key6, key7, key8] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>(); this[key1][key2, key3, key4, key5, key6, key7, key8, key9] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>(); this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10); } } public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>> { public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11] { get { return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] : default(V); } set { if (!ContainsKey(key1)) this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>(); this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] = value; } } public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11) { return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10, key11); } } 

看看Wintellect的PowerCollections ( CodePlex下载 )。 我认为他们的MultiDictionary就是这样的。

这是一个词典字典,所以你有2个键来访问每个对象,主要词典的关键是让你所需的子词典,然后第二个键为子词典来获得所需的项目。 你是这个意思吗?

有什么问题吗?

 新词典<KeyValuePair <object,object>,object> 

我经常使用这个,因为它很短,并提供我需要的语法糖…

 public class MultiKeyDictionary<T1, T2, T3> : Dictionary<T1, Dictionary<T2, T3>> { new public Dictionary<T2, T3> this[T1 key] { get { if (!ContainsKey(key)) Add(key, new Dictionary<T2, T3>()); Dictionary<T2, T3> returnObj; TryGetValue(key, out returnObj); return returnObj; } } } 

要使用它:

 dict[cat][fish] = 9000; 

“猫”键也不一定存在。

我目前只是简单地将键串联成一个string作为解决方法。 当然,这不适用于非string键。 也很想知道答案。

我已经为此search: http : //www.codeproject.com/KB/recipes/multikey-dictionary.aspx 。 我猜这是主要的function相比,使用结构来包含在普通字典中的2个键是你可以稍后参考其中一个键,而不是必须提供2个键。

下面是一个可以用作Dictionary的关键字的成对类的例子。

  public class Pair<T1, T2> { public T1 Left { get; private set; } public T2 Right { get; private set; } public Pair(T1 t1, T2 t2) { Left=t1; Right=t2; } public override bool Equals(object obj) { if(ReferenceEquals(null, obj)) return false; if(ReferenceEquals(this, obj)) return true; if(obj.GetType()!=typeof(Pair<T1, T2>)) return false; return Equals((Pair<T1, T2>)obj); } public bool Equals(Pair<T1, T2> obj) { if(ReferenceEquals(null, obj)) return false; if(ReferenceEquals(this, obj)) return true; return Equals(obj.Left, Left) && Equals(obj.Right, Right); } public override int GetHashCode() { unchecked { return (Left.GetHashCode()*397)^Right.GetHashCode(); } } } 

我想你会需要像class级Tuple2。 确保它的GetHashCode()和Equals()是基于两个包含的元素。

请参阅C#中的元组

你可以使用Dictionary<TKey1,Dictionary<TKey2,TValue>>

你甚至可以inheritance这个:

 public class DualKeyDictionary<TKey1,TKey2,TValue> : Dictionary<TKey1,Dictionary<TKey2,TValue>> 

编辑:这是现在重复的答案。 它的实用性也是有限的。 虽然它“工作”,并提供代码dict[key1][key2] ,有很多“变通办法”让它“只是工作”。

但是,只是踢,一个人可以实现字典虽然,但在这一点上得到了一个细节:

 public class DualKeyDictionary<TKey1, TKey2, TValue> : Dictionary<TKey1, Dictionary<TKey2, TValue>> , IDictionary< object[], TValue > { #region IDictionary<object[],TValue> Members void IDictionary<object[], TValue>.Add( object[] key, TValue value ) { if ( key == null || key.Length != 2 ) throw new ArgumentException( "Invalid Key" ); TKey1 key1 = key[0] as TKey1; TKey2 key2 = key[1] as TKey2; if ( !ContainsKey( key1 ) ) Add( key1, new Dictionary<TKey2, TValue>() ); this[key1][key2] = value; } bool IDictionary<object[], TValue>.ContainsKey( object[] key ) { if ( key == null || key.Length != 2 ) throw new ArgumentException( "Invalid Key" ); TKey1 key1 = key[0] as TKey1; TKey2 key2 = key[1] as TKey2; if ( !ContainsKey( key1 ) ) return false; if ( !this[key1].ContainsKey( key2 ) ) return false; return true; } 

这是我的实现。 我想要隐藏Tuple概念的实现。

  public class TwoKeyDictionary<TKey1, TKey2, TValue> : Dictionary<TwoKey<TKey1, TKey2>, TValue> { public static TwoKey<TKey1, TKey2> Key(TKey1 key1, TKey2 key2) { return new TwoKey<TKey1, TKey2>(key1, key2); } public TValue this[TKey1 key1, TKey2 key2] { get { return this[Key(key1, key2)]; } set { this[Key(key1, key2)] = value; } } public void Add(TKey1 key1, TKey2 key2, TValue value) { Add(Key(key1, key2), value); } public bool ContainsKey(TKey1 key1, TKey2 key2) { return ContainsKey(Key(key1, key2)); } } public class TwoKey<TKey1, TKey2> : Tuple<TKey1, TKey2> { public TwoKey(TKey1 item1, TKey2 item2) : base(item1, item2) { } public override string ToString() { return string.Format("({0},{1})", Item1, Item2); } } 

它有助于保持使用看起来像一个词典

 item.Add(1, "D", 5.6); value = item[1, "D"]; 

如果有人正在寻找一个ToMultiKeyDictionary()这里是一个应用程序的大部分答案(基于赫尔曼的 )的实现:

 public static class Extensions_MultiKeyDictionary { public static MultiKeyDictionary<K1, K2, V> ToMultiKeyDictionary<S, K1, K2, V>(this IEnumerable<S> items, Func<S, K1> key1, Func<S, K2> key2, Func<S, V> value) { var dict = new MultiKeyDictionary<K1, K2, V>(); foreach (S i in items) { dict.Add(key1(i), key2(i), value(i)); } return dict; } public static MultiKeyDictionary<K1, K2, K3, V> ToMultiKeyDictionary<S, K1, K2, K3, V>(this IEnumerable<S> items, Func<S, K1> key1, Func<S, K2> key2, Func<S, K3> key3, Func<S, V> value) { var dict = new MultiKeyDictionary<K1, K2, K3, V>(); foreach (S i in items) { dict.Add(key1(i), key2(i), key3(i), value(i)); } return dict; } } 

下面是使用Tuple类和Dictionary的另一个例子。

  // Setup Dictionary Dictionary<Tuple<string, string>, string> testDictionary = new Dictionary<Tuple<string, string>, string> { {new Tuple<string, string>("key1","key2"), "value1"}, {new Tuple<string, string>("key1","key3"), "value2"}, {new Tuple<string, string>("key2","key3"), "value3"} }; //Query Dictionary public string FindValue(string stuff1, string stuff2) { return testDictionary[Tuple.Create(stuff1, stuff2)]; }