【问题标题】:How to Migrate structs form C++ to C#如何将结构从 C++ 迁移到 C#
【发布时间】:2012-06-21 14:26:12
【问题描述】:

我正在编写一个接收复杂数据结构的 UDP 客户端。 我确实有 C++ 中的结构,但我的程序是 C#

Bitfiels 和联合有很多不同的结构。

有什么方法可以不用手动转换结构吗?

还有在 C# 中实现位域和联合的简单方法吗?

现在我正在为位域使用属性,但这是一项艰巨的工作,极有可能出错。

我提供了一个我现在正在做的简化示例,大约有 50 个结构,每个结构有 100 行代码。

C++ 示例:

typedef struct Message_s
{
    unsigned char var : 2;
    unsigned char var2 : 6;

    union
    {
         struct
         {
            unsigned char sVar;
            unsigned char sVar2;
         }
         char arr[32];
     }    
}Message_t;

示例 C#:

 [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct Message_s
{
    private byte Field1
    public byte var
    {
       get
       {
           return (byte)(Field1 & 0x03);
       }
       set
       {
           Field1 = (byte)((Field1 & ~0x03) | value);
       }
    public byte var2
    {
       get
       {
           return (byte)(Field1 & 0xFC);
       }
       set
       {
           Field1 = (byte)((Field1 & 0x03) | value<<2);
       }
     }
//unions may be possible with properties...
}

【问题讨论】:

  • 关于操作位域,您可能需要查看 System.Collections.Specialized.BitVector32。此外,根据您的操作,您可能希望查看使用适当宽度的枚举并使用 Flags 属性对其进行装饰。
  • 好的,这样怎么样:我编写了一个包含所有结构的管理 C++ DLL,这将允许我从 C# 访问它们,不是吗?另外,另一个问题,C++ 编译器是否保持结构的字节和位顺序?
  • 不幸的是,@ChristianElsner,我有同样的想法,这似乎不起作用。这类结构可以编译,但不能从 C# 访问它们的成员。您通过将struct 替换为value class 使其成为托管结构,但随后您会得到位域和联合的编译器错误。
  • 是否可以使用 COM 访问您的 C++ 结构?

标签: c# c++ struct migration


【解决方案1】:

简短的回答是否定的,似乎没有一种简单的方法可以将 C++ 结构转换为 C# 格式。但是,有一种方法可以在 C# 结构中进行联合;这是一个好主意,还是从头开始学习更好,这取决于您。

要在 C# 结构中获取联合,请使用 LayoutKind.ExplicitFieldOffset 属性:

[StructLayout(LayoutKind.Explicit)]
struct StructTest
{
    // A byte at the beginning of the struct.
    [FieldOffset(0)]
    public byte byte1;

    // Another byte immediately after it.
    [FieldOffset(1)]
    public byte byte2;

    // Now an integer which covers both.
    [FieldOffset(0)]
    public Int16 int1;
}

你可以按照你的期望使用它:

        var foo = new StructTest();
        foo.byte1 = 3;
        foo.byte2 = 6;
        Console.WriteLine("{0}", foo.int1);

        var bar = new StructTest();
        bar.int1 = 5050;
        Console.WriteLine("{0}, {1}", bar.byte1, bar.byte2);

产生1539186, 19

这为您提供了联合结构,但它仍然不能解决 BitFields 的主要问题。对此似乎没有达成共识(当然,除了您已经完成的工作;例如,参见 [1])。使用MarshalAs 属性可能会做一些聪明的事情,将自定义 BitField 类映射到底层结构,但在这种努力下,继续编写自定义类可能会更容易。

【讨论】:

    【解决方案2】:

    作为替代方案,一种在代码中使用非托管 C++ 类型的方法,但它仍然涉及一些类型。在 VS C++ 项目中,创建非托管结构:

    struct Date_s
    {
        union
        {
            struct
            {
                unsigned nWeekDay  : 3;    // 0..7   (3 bits)
                unsigned nMonthDay : 6;    // 0..31  (6 bits)
                unsigned           : 0;    // Force alignment to next boundary.
                unsigned nMonth    : 5;    // 0..12  (5 bits)
                unsigned nYear     : 8;    // 0..100 (8 bits)
            };
            unsigned bob;
        };
    };
    

    现在我们创建一个封装这个结构的托管类型:

    public ref struct Date_ms
    {
        AutoPtr<Date_s> data;
        Date_ms()
        {
            data = new Date_s();
        }
    
        property unsigned nWeekDay
        {
            unsigned get() { return data->nWeekDay; }
            void set(unsigned value) { data->nWeekDay = value; }
        }
        property unsigned nMonthDay
        {
            unsigned get() { return data->nMonthDay; }
            void set(unsigned value) { data->nMonthDay = value; }
        }
        property unsigned nMonth
        {
            unsigned get() { return data->nMonth; }
            void set(unsigned value) { data->nMonth = value; }
        }
        property unsigned nYear
        {
            unsigned get() { return data->nYear; }
            void set(unsigned value) { data->nYear = value; }
        }
        property unsigned bob
        {
            unsigned get() { return data->bob; }
            void set(unsigned value) { data->bob = value; }
        }
    };
    

    那里有一些复制和粘贴操作。我在这里还使用了一个外部项目:来自 Kerr 的 AutoPtr。我还在 AutoPtr 代码中添加了 T* operator=(T* rhs) { return (m_ptr = rhs); } 行(正如 Kerr 帖子的 cmets 中所建议的那样)以使初始化程序工作。

    您现在可以在 C# 代码中使用这个新结构,一切都应该按预期工作:

    var test = new Date_ms();
    test.nMonth = 3;
    test.nMonthDay = 28;
    test.nYear = 98;
    Console.WriteLine("{0}, {1}, {2}", test.nMonth, test.nMonthDay, test.nYear);
    Console.WriteLine("{0}", test.bob);
    

    导致3, 28, 98224

    大警告

    像这样混合托管和非托管代码是危险的,这就是 Kerr 首先编写 AutoPtr 的原因(顺便说一句,他的 blog post 关于这个主题非常值得一读)。这对我有用,但我不能保证它不会导致内存泄漏。您应该明确在部署它之前检查它是否按预期工作。

    【讨论】:

      猜你喜欢
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-05
      • 1970-01-01
      • 2018-06-07
      • 2011-04-03
      • 1970-01-01
      相关资源
      最近更新 更多