积极答复者
C#中要转一个C++中union套struct的联合体,尝试了好几次都没正确运行,希望大家能帮忙看看!

问题
答案
-
上面的结构体定义c++和c#基本差不多 只是C#没有Union
所以在C#中要模拟实现Union要指定成员的内存布局情况,需要结合使用StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute。使用它们的时候必须引用System.Runtime.InteropServices
下面是我写的模拟U的联合
[StructLayout(LayoutKind.Explicit, Size = 12)] struct U { [FieldOffset(0)] public A a; [FieldOffset(0)] public B b; [FieldOffset(0)] public C c; [FieldOffset(0)] public D d; }
我们知道联合中每个数据成员都在相同的内存地址开始,所以我们要通过[FieldOffset(0)]应用到U的每一个成员,意思就是让这些成员处于同一个开始位置。当然,我们得事先告诉.NET这些成员的内存布局由我们来作主,所以要使用LayoutKind.Explicit枚举然后传递给StructLayoutAttribute,并应用到U上,这样.Net就不会再干涉该struct的成员在内存中的布局了。并且我定义了U的SIze为12,当然你也可以不定义U的Size。
认真的活,认真的爱!
- 已编辑 chenrensongModerator 2013年1月11日 7:32
- 已建议为答案 chenrensongModerator 2013年1月11日 7:32
- 已标记为答案 Lisa ZhuModerator 2013年1月22日 2:30
-
[StructLayout(LayoutKind.Sequential)]
public struct A
{
public UInt16 us;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public UInt16[] ast;
}
[StructLayout(LayoutKind.Sequential)]
public struct B
{
//注释1
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast;
// 注释2
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast2;
}
[StructLayout(LayoutKind.Sequential)]
public struct C
{
//注释3
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast;
}
[StructLayout(LayoutKind.Explicit)]
public struct D
{
[FieldOffset(0)]
public B b;
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public C c;
}
chenrensong,你好!感谢你迅速的提供了这种方案,但是我在测试的时候发现,如果结构体内容中包含数组,就容易出现错误。如上所示代码,如果A中包含数组,那么,B、C中都必须包含数组,有且只能包含一个数组,否则在new D的对象的时候都会出现:“未能从程序集“test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中加载类型“D”,因为它在 0 偏移位置处包含一个对象字段,该字段已由一个非对象字段不正确地对齐或重叠。”你好,这个问题其实和它是否有数组没关系的,主要因为引用类型和值类型不同,初始化的时候,值类型会被赋予零值,引用类型会被赋予Null,这2个启始位置无法统一起来,所以会报错。
CLR是不允许值类型与引用类型的域交叠,所以这种写法在C#里面行不通了。
认真的活,认真的爱!
- 已标记为答案 Lisa ZhuModerator 2013年1月22日 2:30
全部回复
-
上面的结构体定义c++和c#基本差不多 只是C#没有Union
所以在C#中要模拟实现Union要指定成员的内存布局情况,需要结合使用StructLayoutAttribute、LayoutKind以及FieldOffsetAttribute。使用它们的时候必须引用System.Runtime.InteropServices
下面是我写的模拟U的联合
[StructLayout(LayoutKind.Explicit, Size = 12)] struct U { [FieldOffset(0)] public A a; [FieldOffset(0)] public B b; [FieldOffset(0)] public C c; [FieldOffset(0)] public D d; }
我们知道联合中每个数据成员都在相同的内存地址开始,所以我们要通过[FieldOffset(0)]应用到U的每一个成员,意思就是让这些成员处于同一个开始位置。当然,我们得事先告诉.NET这些成员的内存布局由我们来作主,所以要使用LayoutKind.Explicit枚举然后传递给StructLayoutAttribute,并应用到U上,这样.Net就不会再干涉该struct的成员在内存中的布局了。并且我定义了U的SIze为12,当然你也可以不定义U的Size。
认真的活,认真的爱!
- 已编辑 chenrensongModerator 2013年1月11日 7:32
- 已建议为答案 chenrensongModerator 2013年1月11日 7:32
- 已标记为答案 Lisa ZhuModerator 2013年1月22日 2:30
-
[StructLayout(LayoutKind.Sequential)]
public struct A
{
public UInt16 us;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public UInt16[] ast;
}
[StructLayout(LayoutKind.Sequential)]
public struct B
{
//注释1
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast;
// 注释2
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast2;
}
[StructLayout(LayoutKind.Sequential)]
public struct C
{
//注释3
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast;
}
[StructLayout(LayoutKind.Explicit)]
public struct D
{
[FieldOffset(0)]
public B b;
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public C c;
}
chenrensong,你好!感谢你迅速的提供了这种方案,但是我在测试的时候发现,如果结构体内容中包含数组,就容易出现错误。如上所示代码,如果A中包含数组,那么,B、C中都必须包含数组,有且只能包含一个数组,否则在new D的对象的时候都会出现:“未能从程序集“test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中加载类型“D”,因为它在 0 偏移位置处包含一个对象字段,该字段已由一个非对象字段不正确地对齐或重叠。” -
[StructLayout(LayoutKind.Sequential)]
public struct A
{
public UInt16 us;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public UInt16[] ast;
}
[StructLayout(LayoutKind.Sequential)]
public struct B
{
//注释1
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast;
// 注释2
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast2;
}
[StructLayout(LayoutKind.Sequential)]
public struct C
{
//注释3
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
//public UInt16[] ast;
}
[StructLayout(LayoutKind.Explicit)]
public struct D
{
[FieldOffset(0)]
public B b;
[FieldOffset(0)]
public A a;
[FieldOffset(0)]
public C c;
}
chenrensong,你好!感谢你迅速的提供了这种方案,但是我在测试的时候发现,如果结构体内容中包含数组,就容易出现错误。如上所示代码,如果A中包含数组,那么,B、C中都必须包含数组,有且只能包含一个数组,否则在new D的对象的时候都会出现:“未能从程序集“test, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”中加载类型“D”,因为它在 0 偏移位置处包含一个对象字段,该字段已由一个非对象字段不正确地对齐或重叠。”你好,这个问题其实和它是否有数组没关系的,主要因为引用类型和值类型不同,初始化的时候,值类型会被赋予零值,引用类型会被赋予Null,这2个启始位置无法统一起来,所以会报错。
CLR是不允许值类型与引用类型的域交叠,所以这种写法在C#里面行不通了。
认真的活,认真的爱!
- 已标记为答案 Lisa ZhuModerator 2013年1月22日 2:30