【问题标题】:C++ union in C#C# 中的 C++ 联合
【发布时间】:2010-09-12 17:03:11
【问题描述】:

我正在将一个用 C++ 编写的库翻译成 C#,并且关键字“union”存在一次。在结构中。

将它翻译成 C# 的正确方法是什么?它有什么作用?它看起来像这样;

struct Foo {
    float bar;

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

【问题讨论】:

  • 这可能更安全,但是当您与提供此类数据结构的 C 库交互时,C# 中的此决定甚至会破坏 C/C++ 结构的基本封装。我正在尝试处理这样的事情: struct LibrarySType { AnotherType *anotherTypeBuff; int oneSetOfFlags; int anotherSetOfFlags;联合 { 结构 { int structMember1; ... } oneUseOfThisLibraryType;结构 { 字符 *structMember2; ... } anotherUseOfThisLibraryType; ... } 你; int64 *更多东西; ... 你明白了} 现在我没有发明这个聪明的数据结构,但它是我需要的供应商 API 的一部分

标签: c# c++ unions


【解决方案1】:

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

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

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

struct tutorial了解更多关于工会的信息

【讨论】:

  • 请注意,这只有在涉及的类型是原始类型(如 int 和 float)时才有效。一旦涉及对象,它就不会让你。
  • 它适用于值类型(如枚举)还是仅适用于原始类型?
  • 如果您有其他复杂类型,则第二个可能是与另一个相互转换的属性。取决于 Union 的使用情况以及它在您要移植的其他代码中的使用方式。
  • "你当然只能使用其中之一。"可能不清楚。您可以同时访问和设置它们,但它们会相互覆盖。我会澄清这一点。
  • @Khoth 您可以在结构的字段中保存任何内容。与显式结构布局相同。
【解决方案2】:

在 C/C++ 中,联合用于覆盖同一内存位置中的不同成员,所以如果你有一个 int 和一个 float 的联合,它们都使用相同的 4 字节内存来存储,显然写入一个会破坏其他(因为 int 和 float 有不同的位布局)。

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

编辑:互操作除外

【讨论】:

  • 这可能是因为 .NET 是更高级别的,您可能不必担心显式序列化数据以及在 little-endian 和 big-endian 机器之间传输。联合提供了一种很好的转换方式,使用前面评论中提到的技巧。
  • 显式布局结构的使用不限于互操作。非引用类型的原语和公共成员可以用完全定义的行为以任意方式覆盖其他非引用类型的原语和公共成员,无论代码是否曾将相关结构传递给外部代码。在这方面,.NET 支持比“现代”C 更低级别的构造。
【解决方案3】:

就个人而言,我会一起忽略 UNION 并将 Killroy 和 Fubar 实现为单独的字段

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

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

【讨论】:

  • 这可能不起作用,具体取决于库的其他部分如何访问它,在某些情况下,您可以写入一个实例并从另一个实例读取以获取相同的数据,但格式略有不同,如果将其拆分为两个不同的变量,该功能将被破坏
  • @KPexEA 我同意。但是,另一方面,如果是联合类型,其中某个标志指示两个字段中的哪一个是合适的,那么这将正常工作。我认为真正重要的是了解联合在每种情况下的用途和目的,然后再决定如何移植它。
  • 如果你想创建一个具有 10 或 20 个联合类型的结构,因为结构是 byval,传递几个字节比每个参数传递 1k 快得多。
【解决方案4】:

如果不了解它的使用方式,您无法真正决定如何处理它。如果它只是用来节省空间,那么你可以忽略它,只使用一个结构体。

但这通常不是使用联合的原因。使用它们有两个常见的原因。一种是提供两种或多种方式来访问相同的数据。例如,int 和 4 字节数组的联合是分离 32 位整数字节的一种(多种)方法。

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

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

【讨论】:

  • 我认为史蒂夫的回答很重要,你真的需要了解你正在移植的特定联合的用法。 C# 中可能还有其他构造可以更优雅的方式满足需求。
  • 虽然我理解@AaronLS 的意思,但最好能提供一个文档链接或一些关于在C# 中优雅 做到这一点的常用方法。我可以猜测一些非常基本的方法,具体取决于用法,例如使用属性返回联合的一个值的“强制转换”,因为它是另一个成员。但是想知道对于常见场景还有哪些其他优雅的方法。
【解决方案5】:

如果您使用 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);

【讨论】:

    【解决方案6】:
    public class Foo
    {
        public float bar;
        public int killroy;
    
        public float fubar
        {
            get{ return (float)killroy;}
            set{ killroy = (int)value;}
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-01-18
      • 1970-01-01
      • 1970-01-01
      • 2012-07-18
      • 2012-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-10
      相关资源
      最近更新 更多