2012-06-21 46 views
3

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

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

有什么办法可以不用手工转换结构?

还有一种简单的方法来实现C#中的位域和联合?

现在我正在使用Bitfields的属性,但这是一些困难的工作,具有很大的错误可能性。

我提供了一个简化的例子,我现在正在做的事情是,每个代码有大约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... 
} 
+0

关于操纵位域,你可能想看看System.Collections.Specialized.BitVector32。另外,根据你在做什么,你可能想看看使用适当宽度的枚举并用Flags属性装饰它。 – JamieSee

+0

好的如何:我写一个混合所有结构的可变C++ DLL,这将允许我从C#访问它们,不是吗? 此外,另一个问题是,C++编译器保持结构的字节和位顺序? –

+0

不幸的是,@ChristianElsner,我有同样的想法,这似乎并没有工作。这些结构体编译,但他们的成员不能从C#访问。通过用'value class'替换'struct',你可以使它成为一个托管结构体,但是你会为位域和联合体产生编译器错误。 – karaken12

回答

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。我还将行T* operator=(T* rhs) { return (m_ptr = rhs); }添加到AutoPtr代码(如克尔的帖子的评论中所建议的)以使初始化工作。在3, 28, 98224

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

结果:

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

大巨头警告

。混合管理,像这样的非托管代码是很危险的,这就是为什么科尔摆在首位写AutoPtr(顺便说一句,他blog post的话题是非常值得一读)。这适用于我,但我不能保证它不会导致内存泄漏。你应该明确地检查它的工作原理,然后部署它。

4

简短的回答是否定的,似乎没有为获得C++结构为C#格式的一个简单的方法。然而,有一种在C#结构中进行联合的方式;不管这是一个好主意,还是最好从头开始,都取决于你。

为了让工会在您使用LayoutKind.Explicit C#结构和FieldOffset属性:

[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类映射到底层结构上,但在这种努力水平下,继续编写自定义类可能会更容易。