C#中的C ++联合


77

我正在将用C ++编写的库转换为C#,并且关键字“联合”存在一次。在结构中。

将其转换为C#的正确方法是什么?它是做什么的?看起来像这样;

struct Foo {
    float bar;

    union {
        int killroy;
        float fubar;
    } as;
}

这样做可能更安全,但是当您与提供此类数据结构的C库进行交互时,C#中的这一决定甚至破坏了C / C ++结构的基本封装。我正在尝试处理如下问题:struct LibrarySType {AnotherType * anotherTypeBuff; int oneSetOfFlags; int anotherSetOfFlags; union {struct {int structMember1; ...} oneUseOfThisLibraryType; struct {char * structMember2; ...} anotherUseOfThisLibraryType; ...} u; int64 *更多 ...您知道了}现在,我没有发明这种聪明的数据结构,但这是我需要的供应商API的一部分
Steve Wart,2010年

Answers:


81

您可以为此使用显式字段布局:

[StructLayout(LayoutKind.Explicit)] 
public struct SampleUnion
{
    [FieldOffset(0)] public float bar;
    [FieldOffset(4)] public int killroy;
    [FieldOffset(4)] public float fubar;
}

未经测试。这个想法是两个变量在结构中的位置相同。当然,您只能使用其中之一。

有关结构教程的联合的更多信息


7
请注意,这仅在涉及的类型是基本类型(如int和float)时才有效。一旦涉及对象,它就不会允许您。
Khoth

12
它适用于值类型(例如枚举)还是仅适用于原始类型?
泰勒·里斯

1
如果您有其他复杂类型,则第二个可能是与另一个相互转换的属性。取决于Union的用法以及在您要移植的其他代码中如何使用Union。
AaronLS

1
“您当然只能使用其中之一。” 可能不清楚。您可以访问和设置它们两者,但是它们会相互覆盖。我要澄清这一点。
Xonatron

@Khoth您可以在结构的字段中保存任何内容。与显式结构布局相同。

22

您不知道如何使用它就无法真正决定如何处理它。如果仅用于节省空间,则可以忽略它而仅使用结构。

但是,这通常不是为什么使用工会的原因。使用它们有两个常见原因。一种是提供两种或更多种访问同一数据的方式。例如,将int与4个字节的数组进行并集是(多种)分离32位整数字节的一种方法。

另一个是当结构中的数据来自外部源(例如网络数据包)时。通常,包含联合的结构的一个元素是ID,该ID告诉您联合的哪种样式有效。

在这两种情况下,您都不能盲目地忽略联合并将其转换为两个(或更多)字段不一致的结构。


3
我认为在史蒂夫的答案中重要的是,您确实需要了解要移植的特定工会的用法。C#中可能还有其他一些构造可以更优雅地满足需求。
AaronLS

3
尽管我了解@AaronLS的含义,但它还是指向文档的链接或一些有关在C#中优雅地实现此目的的常用方法的不错的选择。我可以猜测一些非常基本的方式,具体取决于用法,例如使用属性返回“联合”一个值的“ cast”,因为它是另一个成员。但是,想知道在常见情况下还有哪些其他优雅的方法。
40名侦探,

4

如果您使用union来将其中一种类型的字节映射到另一种字节,则可以在C#中使用BitConverter

float fubar = 125f; 
int killroy = BitConverter.ToInt32(BitConverter.GetBytes(fubar), 0);

要么;

int killroy = 125;
float fubar = BitConverter.ToSingle(BitConverter.GetBytes(killroy), 0);

4

在C / C ++中,并集用于在同一内存位置覆盖不同的成员,因此,如果您有一个int和float的并集,它们都使用相同的4个字节的内存来存储,显然写一个会破坏另一个(因为int和float具有不同的位布局)。

在.Net中,Microsoft选择了更安全的选择,但没有包含此功能。

编辑:互操作性除外


这可能是因为.NET是更高级别的,并且您可能不必担心显式序列化数据以及在低端和大端计算机之间进行传输。联合会提供了一种很好的转换方法,它使用了先前注释中提到的技巧。
tloach

1
显式布局结构的使用不限于互操作。非引用类型的基元和公共成员可以以完全定义的行为以任意方式覆盖其他非引用类型的基元和公共成员,无论代码是否曾经将有问题的结构传递给外部代码。在这方面,.NET支持比“现代”下下级结构
supercat

1

我个人将全部忽略UNION,并将Killroy和Fubar分别实现为单独的字段

public struct Foo
{
    float bar;
    int Kilroy;
    float Fubar;
}

使用UNION可节省int分配的32位内存....这些天不会制作或破坏应用程序。


8
这可能不起作用,这取决于库的其他部分如何访问它,在某些情况下,您可以写入一个实例并从另一个实例读取以获取相同的数据,但格式略有不同,如果您将其分为两个不同的变量
KPexEA,

1
@KPexEA我同意。但是,另一方面,如果是联合的类型,则其中的某个标志指示两个字段中的哪一个是合适的,那么这将很好地工作。我认为真正重要的是在决定如何移植之前,了解每种情况下工会的用途和目的。
AaronLS

4
如果要创建具有10或20个联合类型的结构,因为结构是按字节排列的,传递几个字节要快于每个参数1k。
Brain2000

-1
public class Foo
{
    public float bar;
    public int killroy;

    public float fubar
    {
        get{ return (float)killroy;}
        set{ killroy = (int)value;}
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.