gpt4 book ai didi

c# - 避免切换类型的设计模式或可接受的解决方案

转载 作者:太空狗 更新时间:2023-10-29 21:49:54 25 4
gpt4 key购买 nike

我试图找到一种良好的,干净的设计模式或普遍接受的实现,以处理仅在运行时才知道单个类型的类型枚举。

我知道之前也曾问过类似的问题,但我仍不清楚替代实现相对于开关或一系列if-thens是否具有显着优势。

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

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

如果允许打开“类型”,则解决方案如下:

非工作代码:

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

我认为,此代码的优点是简单,易于阅读且易于维护。

但是,由于在C#中无法使用类型切换功能,因此我按如下方式实现了上述功能:

工作代码:
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;
}

显然,这是一种解决方法,但它也很直接且易于维护。

但是,有几篇文章详细介绍了C#中没有有关类型切换的信息。除了以产生预期结果的方式处理继承方面的困难等之外,我还看到了许多答案,这些答案都说存在一种“更好”的方法,更符合面向对象编程的精神。

提出的常见解决方案是1)使用多态性,或2)使用字典查找。但是实现这两种方法都有其自身的挑战。

关于多态性,下面是一个示例,“如果它能正常工作,那就不好了”代码:

多态的非工作实现:
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...

如果您尝试编译以上代码,则会得到:

'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等。使用“dynamic”关键字不会也可以提供帮助,因为 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...

上面的代码可以工作,并且仍然是直接且可维护的,并且可能更“符合面向对象编程的精神”。

但这真的是“更好的”吗?我认为这会使维护变得更加困难,因为它需要进行更多搜索以查看已实现了哪些类型。

另一种方法是使用字典查找。这样的代码可能看起来像这样:

字典实现:
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...

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

但是,在给出这些示例的情况下,是否有任何更好,更清洁,更容易接受的实现方案,它们比上面的方案具有显着的优势?这些使用多态性或字典查找的实现是否优于使用开关的实现?我并没有真正保存任何代码,而且不确定是否完全提高了代码的可维护性。

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

最佳答案

  • 使用静态的“转换器”(或任何其他转换器)集合,这些转换器都实现一个公共(public)接口(interface)。然后,您可以遍历该集合,询问每个集合是否处理该类型。如果他们这样做,那么请他们这样做。每个转换器仅知道其类型。
  • 使用相同的转换器“静态”集合,但将其保存在按类型键入关键字的“字典”中。然后从字典中按类型请求转换器,并要求它为您转换。
  • 关于c# - 避免切换类型的设计模式或可接受的解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28578105/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com