【问题标题】:Best way to structure class/struct in c#在 C# 中构造类/结构的最佳方法
【发布时间】:2013-01-15 18:23:36
【问题描述】:

我一直在研究旨在以 C 语言实现并分别以 C 风格定义的网络协议规范,例如:

struct some_header {
  uint8_t version;
  uint8_t type;
  uint16_t length;
  uint32_t id; /
};

struct some_struct {
  struct some_header header;
  uint8_t field1; 
  uint8_t pad[3]; /* Pad to 32 bits */
  uint32_t field2;
};

规范中定义了大约数百个类似“some_struct”的结构。

现在的目标是复制 C# 中的定义并围绕这些结构增加一些功能。

假设将它们表示为对象会很好,那么在 C# 中用于表示上述示例中的结构的最佳原语是什么?

  1. 使用 unsafe 子句复制类 C 定义,然后在每个结构周围使用类包装器

  2. 使用 StructLayout 属性来指导托管结构内的布局,并为每个此类结构使用包装类

  3. 使用成熟的类,并且只使用内存或网络流进行有线表示

除了托管 c++ 之外还有其他想法吗?

其次,显然任务本身是非常劳动密集型的,并且在列出的任何情况下都容易出错。是否有任何自动化可以帮助将 C 标头转换为上述解决方案之一?有人可以推荐一个吗?

UPD。

一些图可以更好地描述问题。

(BoxA) --- 通过 TCP/IP 运行的有线协议 --- (BoxB)

假设 BoxA 运行一些用纯 C 编写的代码,BoxB 应该运行用 C# 编写的代码。

Wire 协议代表了这个庞大的规范,它有 100 多个结构来通信 BoxA 和 BoxB。如果使用 Google Protocol Buffers 或 Apache Thrift 定义协议可能会容易得多,但不幸的是,我正在处理按原样定义的标准。

【问题讨论】:

  • 您可能会发现此资源很有帮助:msdn.microsoft.com/en-us/library/4xwz0t37.aspx
  • C# 中的一个字节是一个无符号的 8 位值,因此可以完美地满足您的需求。
  • 显然,我没有把问题说清楚。事实上,我知道如何将 uint8_t 等基本类型转换为 c# 对应类型。但问题是关于以最佳方式将 c 风格的结构迁移到 c# 表示中。

标签: c# class networking struct


【解决方案1】:

恕我直言,最好手动转换它们,如下所示:

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct Header
{
    public Byte version;
    public Byte type;
    public UInt16 length;
    public UInt32 id;
}

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct Struct
{
    public Header header;
    public Byte field1;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=3)]
    public Byte[] pad;
    public UInt32 field2;
}

您将来可能不得不将它们与本机函数一起使用...所以最好将它们保留为结构并直接在您的 .NET 代码中使用它们。如果您必须将它们作为本机方法的参数传递或将结果 IntPtrs 转换回它们 (Marshal.PtrToStructure),它们将非常有用。

【讨论】:

    【解决方案2】:

    是否有任何自动化可以帮助将 C 标头转换为上述解决方案之一?

    使用C预处理器获取一个大文件,然后使用脚本剪切结构定义,然后应用搜索和替换来转换类型并添加属性。

    【讨论】:

      【解决方案3】:

      我的倾向是使用封装字节数组引用和索引的类型,然后有方法读取或写入下一个字节/字/长字/任何内容。从字节流转换为结构类型的代码如下所示:

      myReader = new ByteArrayReader(myArr);
      myHead.version = myReader.getByte();
      myHead.type; = myReader.getByte();
      myHead.length = myReader.getUInt16();
      myHead.id = myReader.getUInt32();
      

      在这种情况下,我假设myHead 将是具有上述公共字段的透明结构。我不建议将标头类型设为类,除非实例实际上要“做”某事。使阅读器类型成为结构可能会提高性能,但在某些情况下可能会导致令人惊讶的语义。框架中最接近的等价物是List<T>.Enumerator,这是出于性能考虑的结构。

      【讨论】:

        【解决方案4】:

        关于自动化问题,我刚刚找到了从 c-header 生成 PInvoke 样式代码的优秀工具

        http://clrinterop.codeplex.com/releases/view/14120#ReviewsAnchor

        具体来说,需要在命令行使用以下工具

        sigimp /lang:cs "path-to-c-header"

        生成代码示例如下:

        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct some_header {
        
        /// uint8_t->unsigned char
        public byte version;
        
        /// uint8_t->unsigned char
        public byte type;
        
        /// uint16_t->unsigned int
        public uint length;
        
        /// uint32_t->unsigned int
        public uint id;
        }
        
        [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi)]
        public some_struct {
        
        
        public some_header header;
        
        /// uint64_t->unsigned int
        public uint xxx;
        
        /// uint32_t->unsigned int
        public uint xxx2;
        
        /// uint8_t->unsigned char
        public byte xxx3;
        
        /// uint8_t->unsigned char
        public byte xxx4;
        
        /// uint8_t[2]
        [System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=2)]
        public string pad;
        
        /// uint32_t->unsigned int
        public uint xxx5;
        
        /// uint32_t->unsigned int
        public uint xxx6;
        }
        

        【讨论】:

          【解决方案5】:

          虽然 C# 有 Struct,但最好的办法是使用 Class。您可以将类用作数据容器,查看贫血域模式,或者您可以在其中包含实现,例如验证和业务逻辑。请参阅:域驱动。

          在处理您的域模型时,请确保接受 SOLID principles

          此外,根据您的领域模型,您当然可以利用抽象、继承等。

          哦。保持简单和直观。

          【讨论】:

          • -1 我看不出这个建议与这个问题有什么关系?
          • 我认为当代码从网络反序列化时,值类型会更好。
          • @PeterWishart - 出于好奇,为什么值类型更可取?
          • @JustinMorgan:使用引用类型时,必须(1)为每个读取的事物构造一个新的不可变对象实例;因为一个人可能会阅读很多东西,这可能会变得昂贵; (2) 返回一个新的可变对象实例,但语义不清楚接收者修改它意味着什么,或者 (3) 要求数据的接收者提供一个可变对象实例来放置数据。中型结构通常比不可变对象更有效,除非一般对象有 3 个或更多的引用。
          • @JustinMorgan 我假设 OP 将使用例如Marshal.PtrToStructure 首先手动将这些对象从网络缓冲区反序列化为托管数据,因此他希望“公共标头标头”不是参考。
          猜你喜欢
          • 1970-01-01
          • 2013-03-07
          • 2011-09-08
          • 1970-01-01
          • 2022-01-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多