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

  • 问题

  • C++ code
    struct{
        unsigned int ia;
        char iac[4];
        short ias[4];
    }A;
    struct{
        unsigned int ib;
        char iac[4];
        short ias[4];
    }B;
    struct{

    }C;
    struct{
        unsigned int id;
    }d;

    union{
        A a;
        B b;
        C c;
        D d
    }U;

    ///////////////////////

    希望大家能帮忙写个正确的C#对应的代码!THX!!!

    2013年1月11日 6:55

答案

  • 上面的结构体定义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。


    认真的活,认真的爱!


    2013年1月11日 7: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#里面行不通了。


    认真的活,认真的爱!

    2013年1月14日 2:58
    版主

全部回复

  • 上面的结构体定义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。


    认真的活,认真的爱!


    2013年1月11日 7: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 偏移位置处包含一个对象字段,该字段已由一个非对象字段不正确地对齐或重叠。”
    2013年1月14日 1:24
  •         [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#里面行不通了。


    认真的活,认真的爱!

    2013年1月14日 2:58
    版主