我正在将用 C++ 编写的库翻译为 C#,并且关键字“union”存在一次。在一个结构中。
将其翻译成 C# 的正确方法是什么?它有什么作用?它看起来像这样;
struct Foo {
float bar;
union {
int killroy;
float fubar;
} as;
}
您可以为此使用显式字段布局:
[StructLayout(LayoutKind.Explicit)]
public struct SampleUnion
{
[FieldOffset(0)] public float bar;
[FieldOffset(4)] public int killroy;
[FieldOffset(4)] public float fubar;
}
未经测试。 这个想法是两个变量在结构中具有相同的位置。 您当然可以只使用其中之一。
有关联合的更多信息,请参见 struct 教程
如果不了解它的使用方式,您就无法真正决定如何处理它。如果它只是为了节省空间,那么你可以忽略它,只使用一个结构体。
然而,这通常不是使用联合的原因。使用它们有两个常见原因。一是提供两种或多种访问相同数据的方式。例如,int 和 4 字节数组的并集是分离 32 位整数字节的(多种)方法之一。
另一种情况是结构体中的数据来自外部源,例如网络数据包。通常,包含联合的结构体的一个元素是一个 ID,它告诉您联合的哪种风格正在生效。
在这两种情况下,您都不能盲目地忽略联合并将其转换为两个(或更多)字段不重合的结构。
在 C/C++ 中,联合用于在同一内存位置覆盖不同的成员,因此,如果您有一个 int 和 float 的联合,它们都使用相同的 4 字节内存来存储,显然写入一个会破坏另一个(因为 int 和 float 有不同的位布局)。
在 .Net 中,微软选择了更安全的选择,并且没有包含此功能。
编辑:互操作除外
如果您使用
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);
您可以编写一个简单的包装器,但在大多数情况下只使用一个对象就不会那么混乱。
public class MyUnion
{
private object _id;
public T GetValue<T>() => (T)_id;
public void SetValue<T>(T value) => _id = value;
}
[System.Runtime.InteropServices.StructLayout(LayoutKind.Explicit)]
struct TestUnion
{
[System.Runtime.InteropServices.FieldOffset(0)]
public int i;
[System.Runtime.InteropServices.FieldOffset(0)]
public double d;
[System.Runtime.InteropServices.FieldOffset(0)]
public char c;
[System.Runtime.InteropServices.FieldOffset(0)]
public byte b;
}
就我个人而言,我会忽略 UNION 并将 Killroy 和 Fubar 作为单独的字段实现
public struct Foo
{
float bar;
int Kilroy;
float Fubar;
}
使用 UNION 可以保存由 int 分配的 32 位内存......现在不会决定应用程序的成败。
public class Foo
{
public float bar;
public int killroy;
public float fubar
{
get{ return (float)killroy;}
set{ killroy = (int)value;}
}
}