c#中的多键字典?

Sup*_*234 112 c# dictionary

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

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

Eam*_*nne 65

在他的回答中我也使用过jason作为jason.但是,我建议您只需将元组定义为结构:

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); 
    }
}
Run Code Online (Sandbox Code Playgroud)

你得到不变性,.GetHashcode并.Equals为自由故,两者(当你在等待C#4.0)是很好的"东经简单...

一个警告然而:默认的GetHashcode实现(有时)只考虑第一个字段,以便确保使第一场最挑剔或者实现GetHashcode自己(例如,使用FieldwiseHasher.Hash(this)来自ValueUtils),否则你可能会遇到可伸缩性问题.

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

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

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

sealed class MyValueObject : ValueObject<MyValueObject> {
    public DayOfWeek day;
    public string NamedPart;
    //properties work fine too
}
Run Code Online (Sandbox Code Playgroud)

...希望能够更容易地为具有值语义的数据提供人类可读的名称,至少在C#的某个未来版本实现具有命名成员的正确元组之前 ; 希望有合适的哈希码;-).

  • 关于.NET中元组的引用与值类型决策的精彩设计:http://msdn.microsoft.com/en-us/magazine/dd942829.aspx#id0400060 (2认同)

jas*_*son 54

我用a Tuple作为键Dictionary.

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

    // implementation details
}
Run Code Online (Sandbox Code Playgroud)

一定要覆盖Equals和GetHashCode定义operator!=并operator==酌情.您可以Tuple根据需要展开以容纳更多项目..NET 4.0将包含一个内置的Tuple.

  • 查看关于多个"多键"字典实现之间性能测试的博客文章[这里](http://www.fyslexicduck.com/2011/05/performance-tests-between-multiple.html) (2认同)

Cha*_*ana 32

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

 Dictionary<key1, Dictionary<key2, TypeObject>> 
Run Code Online (Sandbox Code Playgroud)

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

 public class TwoKeyDictionary<K1, K2, T>: 
        Dictionary<K1, Dictionary<K2, T>> { }
Run Code Online (Sandbox Code Playgroud)

或者,有三把钥匙......

public class ThreeKeyDictionary<K1, K2, K3, T> :
    Dictionary<K1, Dictionary<K2, Dictionary<K3, T>>> { }
Run Code Online (Sandbox Code Playgroud)

  • 天啊...我什至不知道你在说什么。您希望多键字典能够使用*任一*键访问对象吗?这绝不等同于操作所请求的元组。对于你的第二个问题,你真的期望一个完全充实的专业实施,带有所有的花里胡哨的东西,作为问题的答案吗?梦想吧,我为此得到报酬。编写您自己的代码。 (3认同)
  • 这不是多键字典,这是一个非常糟糕的tupled-key字典的实现,因为你无法通过第二个键访问值,也没有它可以访问它们(除非你通过内部字典的值得到它,但是它可以包含多个项目).此外,您的解决方案缺少添加和删除项目的代码. (2认同)

Jür*_*ock 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));
Run Code Online (Sandbox Code Playgroud)

这是代码.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,此实现取决于Tuple.Equals()实现本身:

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

在以下条件下,obj参数被视为等于当前实例:

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


Her*_*eld 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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Adr*_*ong 7

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

  • @ Meta-Knight使用分隔符.咄. (9认同)
  • 使用字符串连接可能很危险.例如,如果你有123和456的int,并连接它们来创建一个键,这将给出"123456".如果你有12和3456,你连接它们,你得到......"123456".哎呀. (5认同)
  • 因为在大多数类中.ToSting()对于所有值(即类型名称)都是相同的.并非所有类型都具有有效的字符串表示形式等. (3认同)

Sim*_*ens 6

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

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


JSB*_*ոգչ 6

有什么不对吗?

new Dictionary<KeyValuePair<object, object>, object>
?

  • 是否有内置的Pair <T1,T2>类? (3认同)
  • 是的,有一些错误:KVP使用[ValueType.GetHashCode](http://msdn.microsoft.com/en-us/library/system.valuetype.gethashcode.aspx) - "如果你调用派生类型的GetHashCode方法,返回值不太可能适合用作哈希表中的键." 对于Dictionary键,KVP是完全不合适的选择,并且可能产生许多冲突. (3认同)

max*_*max 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它:

dict[cat][fish] = 9000;
Run Code Online (Sandbox Code Playgroud)

其中"Cat"键也不一定存在.


Mar*_*uła 5

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