Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 多关键字词典自定义数据结构的正确使用_C#_.net_Dictionary_Hashcode - Fatal编程技术网

C# 多关键字词典自定义数据结构的正确使用

C# 多关键字词典自定义数据结构的正确使用,c#,.net,dictionary,hashcode,C#,.net,Dictionary,Hashcode,在我的应用程序中,有一次我遇到一个类的实例需要有三个字符串键(我使用的是C#3.5,所以我不能使用元组)。通过在线查看,我发现了这个答案,我使用了它的代码: 在根据我的需要定制了它的各个部分之后,最终我的定制类是这样的: public class MultiKeyDictionary<K1, K2, K3, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, V>> { public V this[K1 key

在我的应用程序中,有一次我遇到一个类的实例需要有三个字符串键(我使用的是C#3.5,所以我不能使用元组)。通过在线查看,我发现了这个答案,我使用了它的代码:

在根据我的需要定制了它的各个部分之后,最终我的定制类是这样的:

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 void Add(K1 key1, K2 key2, K3 key3, V value)
    {
        if (!ContainsKey(key1))
            this[key1] = new MultiKeyDictionary<K2, K3, V>();
        if (!this[key1].ContainsKey(key2, key3))
            this[key1][key2] = new Dictionary<K3, V>();
        this[key1][key2][key3] = value;
    }
}
公共类多键字典:字典
{
公共V本[K1键1、K2键2、K3键3]
{
得到
{
return ContainsKey(key1)?此[key1][key2,key3]:默认值(V);
}
设置
{
如果(!ContainsKey(键1))
此[key1]=新的MultiKeyDictionary();
此[key1][key2,key3]=值;
}
}
公共bool ContainsKey(K1键1、K2键2、K3键3)
{
返回base.ContainsKey(键1)和&this[key1].ContainsKey(键2,键3);
}
公共无效添加(K1键1、K2键2、K3键3、V值)
{
如果(!ContainsKey(键1))
此[key1]=新的MultiKeyDictionary();
如果(!this[key1].ContainsKey(key2,key3))
此[key1][key2]=新字典();
此[key1][key2][key3]=值;
}
}
这非常适合我的需要,但我对这个数据结构有几个问题:

1) 因为我实际上是从
字典(K1,Dictionary(K2,V))
继承的,所以假设
GetHashCode
是为我实现的,并且我不需要指定单独的实现,这是否正确?平等者也一样吗

2) 我需要创建自己的自定义类的前提是否正确?因为我不能使用字符串数组或字符串列表,因为这样会有一个
ReferenceEquals
比较,而不是我需要的成员级比较(key1等于key1,key2等于key2,key3等于key3)?

GetHashCode
GetHashCode
方法被用作测试类的两个实例是否相等的“廉价”(快速)方法。为两个相等的实例调用
GetHashCode
,应始终产生相同的结果。因此,如果两个实例调用
GetHashCode
的结果不相同,那么它们就不可能相等,因此没有必要进行更详细(而且更“昂贵”)的比较

[另一方面,如果两个实例具有相同的哈希代码,则需要进行更详细的比较,以确定它们是否实际上相等。]

因此,除非您正在定义“equal”对您的类意味着什么,否则您可能不需要担心
GetHashCode
。“平等”的概念对你的班级来说似乎并不是很有用

课堂设计 我不确定您实现的类是否理想。因为您是从
字典
继承的,所以您继承了一些并不真正“适合”您的类的方法

例如,您的类现在有一个
Keys
方法,该方法返回顶级键(key1),而不是您的类实际表示的三值键

我想知道实现一个聚合字典的类是否比从字典继承的类更好

缺少
Tuple
的另一个选项是定义自己的
TriKey
类(具有3个描述键值的属性),只需使用
字典即可。在这种情况下,您绝对希望为
TriKey
类定义相等,并且您需要使
GetHashCode
与相等的定义保持一致,因为字典查找是使用它的地方之一

杂项

最后一点,有些人可能会考虑过早优化。守则:

this[key1][key2][key3] = value;
。。。将对已经获取的值执行2次查找(因为您已经访问了
this[key1]
this[key1][key2]
)。您可能需要考虑使用本地变量来存储这些中间结果。

例如:

MultiKeyDictionary<K2, K3, V> d1;
if (!TryGetValue(key1, out d1))
{
    d1 = new MultiKeyDictionary<K2, K3, V>();
    this[key1] = d1;
}

// now use d1 rather than "this[key1]"
多键字典d1;
如果(!TryGetValue(键1,输出d1))
{
d1=新的MultiKeyDictionary();
该[key1]=d1;
}
//现在使用d1而不是“this[key1]”

。。。等等。

好吧,创建自己的三重密钥结构来存储密钥是一个不错的计划,但首先让我们看看
KeyValuePair
struct

现在让我们定义自己的
TripleKey
struct:

[Serializable]
public struct TripleKey<TKeyA, TKeyB, TKeyC>
{
    public TKeyA KeyA { get; };
    public TKeyB KeyB { get; };
    public TKeyC KeyC { get; };

    public TripleKey(TKeyA keyA, TKeyB keyB, TKeyC keyC)
    {
        this.KeyA = keyA;
        this.KeyB = keyB;
        this.KeyC = keyC;
    }

    // this code is almost the same as it is in Microsoft implementation
    public override string ToString()
    {
        var sBuilder = new StringBuilder();
        sBuilder.Append('(');
        if (KeyA != null)
        {
            sBuilder.Append(KeyA.ToString());
        }
        sBuilder.Append(", ");
        if (KeyB != null)
        {
            sBuilder.Append(KeyB.ToString());
        }
        sBuilder.Append(", ");
        if (KeyC != null)
        {
            sBuilder.Append(KeyC.ToString());
        }
        sBuilder.Append(')');
        return sBuilder.ToString();
    }
}

public static class TripleKey
{
    public static TripleKey<TKeyA, TKeyB, TKeyC> Create<TKeyA, TKeyB, TKeyC>(TKeyA keyA, TKeyB keyB, TKeyC keyC)
    {
        return new TripleKey<TKeyA, TKeyB, TKeyC>(keyA, keyB, keyC);
    }
}

public class MultiKeyDictionary<TKeyA, TKeyB, TKeyC, TValue> : Dictionary<TripleKey<TKeyA, TKeyB, TKeyC>, TValue>
{
    public TValue this[TKeyA keyA, TKeyB keyB, TKeyC keyC]
    {
        get
        {
            var key = TripleKey.Create(keyA, keyB, keyC);
            return base.ContainsKey(key) ? base[key] : default(TValue);
        }
        set
        {
            var key = TripleKey.Create(keyA, keyB, keyC);
            if (!ContainsKey(key))
                base.Add(key, value);

            this[key] = value;
        }
    }

    public bool ContainsKey(TKeyA keyA, TKeyB keyB, TKeyC keyC)
    {
        var key = TripleKey.Create(keyA, keyB, keyC);

        return base.ContainsKey(key);
    }

    public void Add(TKeyA keyA, TKeyB keyB, TKeyC keyC, TValue value)
    {
        base.Add(TripleKey.Create(keyA, keyB, keyC), value);
    }
}
附言

正如ScottChamberlain正确指出的那样,
ValueType
的方法有其自身的优点和缺点。它使用反射,这意味着它可能会导致性能问题,因此在某些情况下,最好不要依赖struct的
GetHashCode
实现,而是使用自定义实现覆盖它

Eric Lippert的博客中有一篇优秀的文章,名为“”


工作示例:

这可能是实现目标的最简单方法:

public class MultiKeyDictionary<TKey, TValue> : Dictionary<Tuple<TKey, TKey, TKey>, TValue>
{
    public MultiKeyDictionary()
        : base()
    {
    }
    ...
}

class Program
{
    static void Main(string[] args)
    {
        // C# 6.0 syntax
        var multiKeyDictionary = new MultiKeyDictionary<string, int>();
        multiKeyDictionary.Add(Tuple.Create("key1", "key2", "key3"), 36);

        // C# 7.0 syntax (not yet released).
        var multiKeyDictionary1 = new MultiDictionary<string, int>();
        multiKeyDictionary1.Add(("key1", "key2", "key3"), 36);
    }
}
公共类多键字典:字典
{
公共多键词典()
:base()
{
}
...
}
班级计划
{
静态void Main(字符串[]参数)
{
//C#6.0语法
var multiKeyDictionary=新的multiKeyDictionary();
添加(Tuple.Create(“key1”、“key2”、“key3”),36);
//C#7.0语法(尚未发布)。
var multiKeyDictionary1=新的多字典();
多键词典1.添加((“键1”、“键2”、“键3”),36);
}
}

当C#7.0发布时,您可以使用漂亮的新元组声明。

使用
字典
直接解决方案,或者您可以使用
列表
also@HadiHassan:这在问题中得到了明确的解决-在C#4之前,
Tuple
类不可用。@GaryMcGill我认为3个键的Tuple可以很容易地完成,但我不认为构建一个数据结构(dictionary of dictionary of dictionary)来表示数据
public class MultiKeyDictionary<TKey, TValue> : Dictionary<Tuple<TKey, TKey, TKey>, TValue>
{
    public MultiKeyDictionary()
        : base()
    {
    }
    ...
}

class Program
{
    static void Main(string[] args)
    {
        // C# 6.0 syntax
        var multiKeyDictionary = new MultiKeyDictionary<string, int>();
        multiKeyDictionary.Add(Tuple.Create("key1", "key2", "key3"), 36);

        // C# 7.0 syntax (not yet released).
        var multiKeyDictionary1 = new MultiDictionary<string, int>();
        multiKeyDictionary1.Add(("key1", "key2", "key3"), 36);
    }
}