C#继承设计模式问题

Posted

技术标签:

【中文标题】C#继承设计模式问题【英文标题】:C# inheritance design-pattern question 【发布时间】:2010-12-06 20:41:43 【问题描述】:

我正在存储不同格式和长度的数据。我有一个类层次结构来表示这一点:

abstract class BaseDataFormat 
    abstract void InitalizeFromBytes(byte [] );


class DataFormat1 : BaseDataFormat...  // data stored in 3 bytes
class DataFormat2 : BaseDataFormat... /// data stored in 4 bytes

当我读取数据时(比如从字节 []),我需要知道长度(字节数)以适当地读取和创建相应的类型。 DataFormat1 和 DataFormat2 的长度不同,那么如何在运行时获取这些信息呢?即。

Fcn<DATAFORMATTYPE>(...)
 where DATAFORMATTYPE: BaseDataFormat, new();


DATAFORMATTYPE tmp = new DATAFORMATTYPE();
tmp.InitalizeFromBytes(ReadFromByteBuffer( ... someLength));


如何根据 DATAFORMATTYPE 对要读取的正确字节数进行编码?每个的长度感觉应该是数据格式类型的静态属性,但是静态属性不能被派生类覆盖,所以我不知道该怎么做。

长度可以编码为实例属性,但这似乎应该是在类级别(即静态)编码的知识。有没有设计模式可以解决这个问题?

谢谢

【问题讨论】:

【参考方案1】:

也许在BaseDataFormat 中有一个名为DataLength 的属性。要强制所有继承者设置一个值,请在 BaseDataFormat 的构造函数中获取长度,然后将该属性设置为数据长度。

例子:

abstract class BaseDataFormat
 
    BaseDataFormat(int dataLength)
    
        DataLength = dataLength;
    

    public int DataLength  get; private set; 
    abstract void InitalizeFromBytes(byte [] );


class DataFormat1 : BaseDataFormat

    public DataFormat1() : base(3)
    
        // ...
    

当然,它不是在静态级别,但它是强制所有继承者的东西。

另一种方法是如 VirtualBlackFox 建议的那样,用属性装饰类。唯一的问题是 AFAIK 你不能将属性强制到一个类上,就像抽象成员一样。

【讨论】:

【参考方案2】:

你怎么知道读取数据时要实例化哪个子类?从串行数据创建对象的更通用的解决方案是使用工厂模式。

这方面的一个例子是在:http://en.wikipedia.org/wiki/Factory_method_pattern#Encapsulation

【讨论】:

【参考方案3】:

我将为每个 DataFormat 构建 Factory 对象。或者也许让DataFormat 成为工厂,并调用实际的数据对象Datum 或类似的东西。然后你可以实例化工厂,并将字节缓冲区传递给它,它可以读取必要的字节并构造实际的数据对象。

【讨论】:

我喜欢这种方法。当您不想被实例化的细节束缚时的好模式;并且可以将字节缓冲区传递给它并让它弄清楚。 无论特定问题(在哪里存储字节数)的内部解决方案是什么,工厂模式都非常明显。【参考方案4】:

如果您真的想要强制它是类的属性而不是实例的属性,那么最好在类上使用属性。

但是您需要从中构建缓存,因为与直接访问相比,反射非常慢。

[AttributeUsage(AttributeTargets.Class)]
class ByteCountAttribute : Attribute

    public int Value  get; private set; 
    public ByteCountAttribute(int count)
    
        Value = count;
    


[ByteCount(5)]
class DataFormat1 : BaseDataFormat  // data stored in 3 bytes
[ByteCount(6)]
class DataFormat2 : BaseDataFormat /// data stored in 4 bytes

static Dictionary<Type, int> s_typeCache = new Dictionary<Type,int>();
public static int GetByteCount<T>() where T : BaseDataFormat

    int result;
    var type = typeof(T);
    if (!s_typeCache.TryGetValue(type, out result))
    
        var atts = type.GetCustomAttributes(typeof(ByteCountAttribute), false);
        result = ((ByteCountAttribute)atts[0]).Value;
        s_typeCache.Add(type, result);
    
    return result;

(代码没有错误管理,但可以工作)

正如 darkassassin93 所说,这种方法的一个问题是你不能强制它的存在,所以你必须证明这个属性是必要的,而不是编译器为你完成工作的抽象属性。

【讨论】:

【参考方案5】:

您所追求的称为通用值适配器。您可以定义一堆只公开某些常量的类,例如:

public static class Dimensions

  public readonly struct Two : IInteger
  
    public int Value => 2;
  

  public readonly struct Three : IInteger
  
    public int Value => 3;
  

然后,您可以定义一个将这些IInteger 类型之一作为类型参数的类,并在运行时简单地提取它们的值:

public class Vector<T, D>
  where D : IInteger, new()

  protected T[] data;

  public Vector()
  
    data = new T[new D().Value];
  

你可以用这个

var x = new Vector<float, Dimensions.Two>();

【讨论】:

以上是关于C#继承设计模式问题的主要内容,如果未能解决你的问题,请参考以下文章

在设计 C# 类库时,我应该何时选择继承而不是接口? [关闭]

C#的扩展方法解析

C# 自定义控件无法查看视图设计:文件中的类都不能进行设计,因此未能为该文件显示设计器

浅谈 C# 多态的魅力 - 虚方法抽象接口实现

c#类中定义public virtual ListItem SelectedItem get; 只能get不可以set,请问有啥方式可以set么?

设计模式(C#)——桥接模式