Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.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
.net C语言中的不透明字典键模式#_.net_Design Patterns_Generics_C# 3.0 - Fatal编程技术网

.net C语言中的不透明字典键模式#

.net C语言中的不透明字典键模式#,.net,design-patterns,generics,c#-3.0,.net,Design Patterns,Generics,C# 3.0,我遇到过很多这样的情况:访问键控集合(如字典)中的项的模式由于键的类型不是简单的类型(string、int、double等),并且不是您希望提升到实际命名类的类型而受到阻碍 C#3.0引入了编译器自动生成的匿名类型的概念。与struct不同,这些动态生成的类提供了Equals()和GetHashCode()的实现,这两个类设计用于.NET中的字典和哈希表实现 我劫持了这个特性来创建一个不透明的密钥——本质上是一个泛型类,它允许您通过提供作为密钥一部分的类型来动态创建密钥——并使用一个匿名类来实际

我遇到过很多这样的情况:访问键控集合(如字典)中的项的模式由于键的类型不是简单的类型(string、int、double等),并且不是您希望提升到实际命名类的类型而受到阻碍

C#3.0引入了编译器自动生成的匿名类型的概念。与
struct
不同,这些动态生成的类提供了
Equals()
GetHashCode()
的实现,这两个类设计用于.NET中的字典和哈希表实现

我劫持了这个特性来创建一个不透明的密钥——本质上是一个泛型类,它允许您通过提供作为密钥一部分的类型来动态创建密钥——并使用一个匿名类来实际提供Equals/GetHashCode行为。此类的目的只是提供一种简单的方法,将多个值一起用作字典或哈希表中的键。它不是用来提供有意义的应用程序逻辑或数据操作的类

该模式的好处是,它可以轻松实现始终提供适当的相等性和散列行为的复合键。它还可以很容易地扩展到任意维的键(不过许多C#至少可以解析为模板参数)。我们甚至可以通过允许继承OpaqueKey类来进行改进,这样就可以为它的属性和构造函数参数指定更具指导意义的名称

我担心这种模式可能会产生一些我没有考虑到的意外后果或隐藏的陷阱

以下不透明代码可能不受欢迎,有什么原因吗?

public class OpaqueKey<A,B>
{
    private readonly object m_Key;

    // Not strictly necessary, but possibly convenient...
    public A First { get; private set; }
    public B Second { get; private set; }

    public OpaqueKey( A k1, B k2 )
    {
        m_Key = new { K1 = k1, K2 = k2 };
        First = k1;
        Second = k2;
    }

    public override bool Equals(object obj)
    {
        var otherKey = obj as OpaqueKey<A, B>;
        return otherKey == null ? false : m_Key.Equals( otherKey.m_Key );
    }

    public override int GetHashCode()
    {
        return m_Key.GetHashCode();
    }
}


public static void TrivialTestCase()
{
    var dict = new Dictionary<OpaqueKey<string,string>, string>();

    dict.Add(new OpaqueKey<string, string>("A", "B"), "AB");
    dict.Add(new OpaqueKey<string, string>("A", "C"), "AC");
    dict.Add(new OpaqueKey<string, string>("A", "D"), "AD");
    dict.Add(new OpaqueKey<string, string>("A", "E"), "AE");

    var value = dict[new OpaqueKey<string,string>("A","D")];

    Debug.Assert( value == "AD" ); // trivial test case...
}
是否有我在实施过程中未考虑的边缘案例?

public class OpaqueKey<A,B>
{
    private readonly object m_Key;

    // Not strictly necessary, but possibly convenient...
    public A First { get; private set; }
    public B Second { get; private set; }

    public OpaqueKey( A k1, B k2 )
    {
        m_Key = new { K1 = k1, K2 = k2 };
        First = k1;
        Second = k2;
    }

    public override bool Equals(object obj)
    {
        var otherKey = obj as OpaqueKey<A, B>;
        return otherKey == null ? false : m_Key.Equals( otherKey.m_Key );
    }

    public override int GetHashCode()
    {
        return m_Key.GetHashCode();
    }
}


public static void TrivialTestCase()
{
    var dict = new Dictionary<OpaqueKey<string,string>, string>();

    dict.Add(new OpaqueKey<string, string>("A", "B"), "AB");
    dict.Add(new OpaqueKey<string, string>("A", "C"), "AC");
    dict.Add(new OpaqueKey<string, string>("A", "D"), "AD");
    dict.Add(new OpaqueKey<string, string>("A", "E"), "AE");

    var value = dict[new OpaqueKey<string,string>("A","D")];

    Debug.Assert( value == "AD" ); // trivial test case...
}
有没有更简单的方法来实现相同的功能?

public class OpaqueKey<A,B>
{
    private readonly object m_Key;

    // Not strictly necessary, but possibly convenient...
    public A First { get; private set; }
    public B Second { get; private set; }

    public OpaqueKey( A k1, B k2 )
    {
        m_Key = new { K1 = k1, K2 = k2 };
        First = k1;
        Second = k2;
    }

    public override bool Equals(object obj)
    {
        var otherKey = obj as OpaqueKey<A, B>;
        return otherKey == null ? false : m_Key.Equals( otherKey.m_Key );
    }

    public override int GetHashCode()
    {
        return m_Key.GetHashCode();
    }
}


public static void TrivialTestCase()
{
    var dict = new Dictionary<OpaqueKey<string,string>, string>();

    dict.Add(new OpaqueKey<string, string>("A", "B"), "AB");
    dict.Add(new OpaqueKey<string, string>("A", "C"), "AC");
    dict.Add(new OpaqueKey<string, string>("A", "D"), "AD");
    dict.Add(new OpaqueKey<string, string>("A", "E"), "AE");

    var value = dict[new OpaqueKey<string,string>("A","D")];

    Debug.Assert( value == "AD" ); // trivial test case...
}
公共类不透明密钥
{
私有只读对象m_密钥;
//不是严格必要的,但可能很方便。。。
第一个公共集{get;private set;}
公共B第二个{get;私有集;}
公共不透明密钥(A k1,B k2)
{
m_Key=new{K1=K1,K2=K2};
第一个=k1;
秒=k2;
}
公共覆盖布尔等于(对象对象对象)
{
var otherKey=obj作为不透明键;
返回otherKey==null?false:m_Key.Equals(otherKey.m_Key);
}
公共覆盖int GetHashCode()
{
返回m_Key.GetHashCode();
}
}
公共静态测试用例()
{
var dict=新字典();
添加(新的不透明键(“A”、“B”)、“AB”);
添加(新的不透明键(“A”、“C”)、“AC”);
添加(新的不透明(“A”、“D”)、“AD”);
添加(新的不透明(“A”、“E”)、“AE”);
var值=dict[新的不透明键(“A”、“D”)];
Assert(value==“AD”);//简单的测试用例。。。
}

以下不透明代码可能不受欢迎,有什么原因吗

->您正在存储两倍于字符串的数据

有没有更简单的方法来实现相同的功能


->使用结构可以避免匿名类型和将字符串存储两倍。Equals/GetHashcode就可以了。

从我所知道的,您可以使用通用元组类,并且不需要内部匿名类。元组类在解决方案的其他地方可能很有用(这就是.NET4.0将内置通用元组类的原因)。Equals可以比较First和Second的值,而GetHashCode将组合元组成员的hashcodes

        //4.0 Beta1 GetHashCode implementation
        int a = 5; int b = 10;
        Tuple<int, int> t = new Tuple<int, int>(a, b);

        Console.WriteLine(t.GetHashCode() == (((a << 5) + a) ^ b));
//4.0 Beta1 GetHashCode实现
INTA=5;int b=10;
Tuple t=新的Tuple(a,b);

Console.WriteLine(t.GetHashCode()==((a你考虑过将字典从XML序列化到XML的边缘情况吗?

使用KeyValuePair作为键的类型不会有同样的影响吗?

我自己也做过同样的事情,没有任何不利的后果。我的意见是:去做吧。我见过这样一个经常被称为“元组”的类在其他语言和microsoft内部类中,但我自己一直称这种东西为
CompositeKey
。我从未像您那样实现过它,依赖匿名类型系统进行比较和散列

您是否考虑过您的OpaqueKey是否需要序列化?值得担心的是,匿名类型上GetHashcode的默认实现在.net版本之间会发生变化

在确保您的值是有效的只读方面,您做了正确的事情


现在还没有更简单的方法可以做到这一点。我见过很多字典都有同样的效果,但这要麻烦得多,因为如果子字典不存在,你必须创建它。

我看到的缺点是:

  • 正如纪尧姆所指出的,将密钥数据存储两次(一次在第一个和第二个字段中,一次在m_key中)
  • 可以说,代码可读性降低了(键相当冗长,新开发人员需要熟悉OpaqueKey类和概念)
  • C#编译器的未来版本可能会更改匿名类型的默认GetHashCode实现。尽管您对当前结果感到满意,但我不认为逻辑保证永远不会更改
  • 我认为这并不简单,但我认为在大多数情况下,最好只为您需要的每种类型的键创建一个特定的类(或结构)。这样:

  • 你不会像使用OpaqueKey那样浪费内存
  • 它更显式,因此代码可读性更好,并且对于重要的Equals和GetHashCode方法,您不需要依赖C#编译器匿名类型创建逻辑
  • 您可以针对每种类型的复合键进行优化。(例如,某些复合键的字段可能不区分大小写。)