c#中的多键字典?

时间:2009-07-23 13:41:34

标签: c# dictionary

我知道BCL中没有一个,但有人能指出我的开源好吗?

By Multi我指的是2把钥匙。 ; - )

16 个答案:

答案 0 :(得分:64)

我也使用jason in his answer作为元组。但是,我建议您只需将元组定义为结构:

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实施(有时)only considers the first field因此请务必使第一个字段最具辨别力或自己实施GetHashcode(例如,使用ValueUtils中的FieldwiseHasher.Hash(this),否则您可能会遇到可伸缩性问题。

此外,您可以避免使用往往使问题复杂化的空值(如果您确实需要空值,则只需使Tuple<>可以为空)。稍微偏离主题,我是唯一一个对框架级别缺乏对非空引用的支持而烦恼的人吗?我在大型项目上工作,偶尔会在某个地方出现一个无法实现的小问题 - 而且嘿嘿,你得到一个空引用异常 - 但是有一个堆栈跟踪指向引用的第一个用法,而不是实际有缺陷的代码

当然,.NET 4.0现在还很老了;我们大多数人都可以使用.NET 4.0的元组。

编辑: 解决.NET为我编写的ValueUtils结构提供的不良GetHashCode实现,这也允许您使用真实姓名作为您的多字段键;这意味着你可以写一些类似的东西:

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

...希望能够更容易地为具有值语义的数据提供人类可读的名称,至少在some future version of C# implements proper tuples with named members之前;希望有合适的哈希码; - )。

答案 1 :(得分:54)

我使用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

答案 2 :(得分:31)

元组将在.Net 4.0中。在那之前,你也可以使用

 Dictionary<key1, Dictionary<key2, TypeObject>> 

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

 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>>> { }

答案 3 :(得分:18)

这里有很多好的解决方案, 我在这里缺少的是一个基于Tuple类型构建的实现,所以我自己写了一个。

由于它只是继承自Dictionary<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参数被视为等于当前实例:

  • 这是一个元组对象。
  • 它的两个组件与当前实例的类型相同。
  • 它的两个组件与当前实例的组件相同。等式由每个组件的默认对象相等比较器确定。

答案 4 :(得分:11)

我写了并成功地使用了它。

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);
    }
}

答案 5 :(得分:7)

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

答案 6 :(得分:6)

看看Wintellect的PowerCollectionsCodePlex download)。我认为他们的MultiDictionary就是这样的。

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

答案 7 :(得分:6)

<{p>

new Dictionary<KeyValuePair<object, object>, object>
是否有问题?

答案 8 :(得分:6)

我经常使用它,因为它很短并且提供了我需要的语法糖......

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;

其中“Cat”键也不一定存在。

答案 9 :(得分:4)

我用Google搜索了这个:http://www.codeproject.com/KB/recipes/multikey-dictionary.aspx。我想这与使用struct在常规字典中包含2个键相比的主要特征是,您可以稍后通过其中一个键引用,而不必提供2个键。

答案 10 :(得分:3)

如果有人正在寻找ToMultiKeyDictionary()这里的实施应该适用于大多数答案(基于Herman's):

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; 
    }
}

答案 11 :(得分:2)

我认为你需要一个类似Tuple2的课程。确保它是GetHashCode()和Equals()基于两个包含的元素。

请参阅Tuples in C#

答案 12 :(得分:2)

您可以使用Dictionary<TKey1,Dictionary<TKey2,TValue>>吗?

你甚至可以将其子类化:

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

编辑:现在这是一个重复的答案。它的实用性也受到限制。虽然它确实“有效”并且提供了代码dict[key1][key2]的能力,但是有很多“变通办法”可以让它“正常工作”。

HOWEVER:只是为了踢,但仍然可以实现Dictionary,但此时它有点冗长:

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;
    }

答案 13 :(得分:2)

这是一个充实的对类示例,它可以用作词典的关键。

  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();
      }
    }
  }

答案 14 :(得分:1)

这是我的实施。我想要隐藏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"];

答案 15 :(得分:0)

这是使用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)];
    }