避免切换类型的设计模式或公认的解决方案 [英] Design Pattern or Accepted Solutions for Avoiding Switching on Types

查看:23
本文介绍了避免切换类型的设计模式或公认的解决方案的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找到一个好的、干净的设计模式或普遍接受的实现来处理类型的枚举,其中单个类型仅在运行时才知道.

I'm trying to find a good, clean design pattern or commonly accepted implementation to deal with an enumeration of types where the individual type is known only at runtime.

我知道以前有人问过类似的问题,但我仍然不清楚替代实现是否比 switch 或一系列 if-thens 具有显着优势.

I know similar questions have been asked before, but it's still not clear to me that the alternate implementations have significant advantages over a switch, or a series of if-thens.

首先,我要演示几个实现,然后我要问一个问题:这些实现比简单开关更好还是更受欢迎?如果是这样,为什么?如果没有,为什么不呢?

First, I'm going to demonstrate a few implementations, and then I'm going to ask the question: Are these implementations better than or preferred over the simple switch? If so, why? If not, why not?

在我的应用程序中,我通过流发送和接收数据.在运行时,我通过序列化接收到一个数据结构,该结构描述了哪些字段位于我的二进制数据中.这包括字段中数据的类型,即 Int32、Bool、Double 等.在设计时,我只知道数据可能是几种类型之一.我需要从流中读取字段并适当地处理数据.

In my application I send and receive data over a stream. At run time, I receive a data structure via serialization that describes what fields are located within my binary data. This includes the Type of the data in the field, i.e. Int32, Bool, Double, etc. At design time, all I know is that the data may be in one of several types. I need to read the fields from the stream and deal with the data appropriately.

如果允许打开类型,解决方案可能是如下:

If switching on Types were allowed, a solution could be as follows:

非工作代码:

object ReadDataField(byte [] buff, ref int position, 
    Dictionary<int, Type> fields)
{
    object value;
    int field = buff[position];
    position++;

    switch(fields[field])
    {
        case typeof(Int32):
        {
            value = (Int32)BitConverter.ToInt32(buff, position);
            position += sizeof(Int32);
            break;
        }
        case typeof(Int16):
        {
            value = (Int16)BitConverter.ToInt16(buff, position);
            position += sizeof(Int16);
            break;
        }
        // Etc...
    }

    return value;
}

在我看来,这段代码的优点是直观、易读、易维护.

In my opinion, this code has the advantage of being straightforward, easy to read, and simple to maintain.

但是,由于在 C# 中无法打开类型,因此我按如下方式实现了上述内容:

However, as switching on Types is not available in C# I implemented the above as follows:

工作代码:

enum RawDataTypes
{
    Int32,
    Int16,
    Double,
    Single,
    etc.
}

object ReadDataField(byte [] buff, ref int position, 
    Dictionary<int, RawDataTypes> fields)
{
    object value;
    int field = buff[position];
    position++;

    switch(fields[field])
    {
        case RawDataTypes.Int32:
        {
            value = (int)BitConverter.ToInt32(buff, position);
            position += sizeof(int);
            break;
        }
        case RawDataTypes.Int16:
        {
            value = (Int16)BitConverter.ToInt16(buff, position);
            position += sizeof(Int16);
            break;
        }
        // Etc.
    }

    return value;
}

这显然是一种变通方法,但也很简单且易于维护.

This is clearly a work-around, but it is also straightforward and easy to maintain.

但是,有几篇文章详细介绍了 C# 中不提供类型的切换.除了难以以产生预期结果的方式处理继承等问题外,我还看到许多答案说有一种更好"的方法更符合面向对象编程的精神.

However, there are several articles detailing switching on Types is not available in C#. And besides the difficulty dealing with inheritance in manner that yields an expected result, etc., I've seen many answers that have said there is a much "better" approach that is more in line with the spirit of object oriented programming.

提出的常见解决方案是 1) 使用多态,或 2) 使用字典查找.但实施其中任何一个都有其自身的挑战.

The common solutions proposed are 1) use polymorphism, or 2) use a dictionary lookup. But implementing either has its own challenges.

关于多态性,下面是一个如果能工作就好了"的代码示例:

Regarding polymorphism, the following is an example of "wouldn't it be nice if it worked" code:

多态的非工作实现:

object ReadDataField(byte [] buff, int position,
    Dictionary<int, Type> fields)
{
    int field = buff[position];
    position++;

    object value = Activator.CreateInstance(fields[field]);
    // Here we're trying to use an extension method on the raw data type.
    value.ReadRawData(buff, ref position);

    return value;
}

public static Int32 ReadRawData(this Int32 value, byte[] buff, ref int position)
{
    value = BitConverter.ToInt32(buff, position);
    position += sizeof(Int32);

    return value;
}

public static Int16 ReadRawData(this Int16 value, byte[] buff, ref int position)
{
    value = BitConverter.ToInt16 (buff, position);
    position += sizeof(Int16 );

    return value;
}

// Additional methods for each type...

如果你尝试编译上面的代码,你会得到:

If you try to compile the above code you'll get:

'object' 不包含'ReadRawData' 的定义,并且最佳扩展方法重载'RawDataFieldExtensions.ReadRawData(short, byte[], ref int)' 在等等等等中有一些无效的参数...

'object' does not contain a definition for 'ReadRawData' and the best extension method overload 'RawDataFieldExtensions.ReadRawData(short, byte[], ref int)' has some invalid arguments in blah blah...

您不能子类化原始数据类型来添加功能,因为它们是密封的,因此扩展方法似乎是一种选择.但是,扩展方法不会从对象"转换为实际类型,即使调用 value.GetType() 返回底层类型:System.Int32、System.Int16 等.使用动态"关键字不会帮助,或者,因为 你不能在动态类型上使用扩展方法.

You can't subclass the raw data types to add the functionality, because they're sealed, so extension methods seemed like an option. However, the extension methods won't convert from 'object' to the actual type, even though calling value.GetType() returns the underlying type: System.Int32, System.Int16, etc. Using the 'dynamic' keyword doesn't help, either, because you can't use extension methods on a dynamic type.

上述可以通过将对象本身的实例作为参数传递给具有多态参数的方法来实现:

多态性的工作实现:

object ReadDataField(byte [] buff, int position,
    Dictionary<int, Type> fields)
{
    int field = buff[position];
    position++;

    dynamic value = Activator.CreateInstance(fields[field]);
    // Here the object is passed to an overloaded method.
    value = ReadRawData(value, buff, ref position);

    return value;
}

public static Int32 ReadRawData(Int32 value, byte[] buff, ref int position)
{
    value = BitConverter.ToInt32(buff, position);
    position += sizeof(Int32);

    return value;
}

public static Int16 ReadRawData(Int16 value, byte[] buff, ref int position)
{
    value = BitConverter.ToInt16 (buff, position);
    position += sizeof(Int16 );

    return value;
}

// Additional methods for each type...

上面的代码有效,并且仍然简单易行,并且可能更本着面向对象编程的精神".

The above code works and is still straightforward and maintainable, and probably more "in the spirit of object oriented programming."

但它真的更好"吗?我认为这使得它更加难以维护,因为它需要更多的搜索才能查看已实现的类型.

But is it really any "better?" I would argue that it makes it more difficult to maintain, as it requires more searching to see which types have been implemented.

另一种方法是使用字典查找.这样的代码可能如下所示:

An alternate approach is to use a dictionary lookup. Such code might look like this:

字典实现:

delegate object ReadDelegate(byte [] buff, ref int position);

static Dictionary<Type, ReadDelegate> readers = new Dictionary<Type, ReadDelegate>
{
    { typeof(Int32), ReadInt32 },
    { typeof(Int16), ReadInt16 },
    // Etc...
};

object ReadDataField(byte [] buff, int position,
    Dictionary<int, Type> fields)
{
    int field = buff[position];
    position++;

    object value = readers[fields[field]](buff, ref position);

    return value;
}

public static object ReadInt32(byte[] buff, ref int position)
{
    Int32 value = BitConverter.ToInt32(buff, position);
    position += sizeof(Int32);

    return value;
}

public static object ReadInt16(byte[] buff, ref int position)
{
    return BitConverter.ToInt16(buff, position);
    position += sizeof(Int16);

    return value;
}

// Additional methods for each type...

在我看来,字典实现相对于多态解决方案的一个优势是它列出了可以在一个易于阅读的位置处理的所有类型.这对可维护性很有用.

An advantage of the dictionary implementation, in my opinion, over the polymorphic solutions is that it lists all of the types that can be handled in one easy to read location. This is useful for maintainability.

但是,鉴于这些示例,是否有任何更好、更简洁、更被接受等的实现方式比上述实现方式具有显着优势?这些使用多态性或字典查找的实现是否优于使用开关?我并没有真正保存任何代码,我也不确定我是否增加了代码的可维护性.

However, given these examples, are there any better, cleaner, more accepted, etc. implementations that have significant advantage over the above? Are these implementations using polymorphism or a dictionary lookup preferred over using a switch? I'm not really saving any code, and I'm not sure I've increased the maintainability of the code at all.

无论如何,我仍然需要用自己的方法枚举每种类型.多态性将条件推迟到语言本身,而不是通过 switch 或 if-then 来明确.使用字典依赖于内部条件来进行自己的查找.说到底,有什么区别呢?

In any case, I still need to enumerate each of the types with its own method. Polymorphism is deferring the conditional to the language itself, rather than being explicit with a switch or an if-then. Using a dictionary is relying on the internal conditionals to do its own lookup. At the end of the day, what's the difference?

推荐答案

  1. 使用所有实现公共接口的转换器"(或其他)的静态集合.然后您可以遍历该集合,询问每个集合是否处理该类型.如果他们这样做,那么请他们这样做.每个转换器只知道自己的类型.
  2. 使用相同的静态转换器集合",但将它们保存在按类型键入的字典中.然后从字典中按类型请求转换器,并要求它为您转换.

这篇关于避免切换类型的设计模式或公认的解决方案的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆