嗯,C++ 中实际上有两种变体:boost::variant 和 COM 变体。该解决方案或多或少遵循相同的想法,但前者更复杂。我希望您打算使用后者。
首先我要告诉你,如果可能的话,你不应该使用它。也就是说,这就是你要做的:-)
变体和互操作
如果您需要相同的字节表示形式,有时会在互操作中使用变体。
如果您正在处理互操作,请务必查看VariantWrapper
MSDN 上的课程并使其像那样工作。
变体和移植注意事项
变体主要用在 API 中,通常如下所示:
void Foo(SomeEnum operation, Variant data);
在C++中这样做的原因是因为没有基础object
类,因此你需要这样的东西。移植的最简单方法是将签名更改为:
void Foo(SomeEnum operation, object data);
但是,如果您无论如何都要移植,您也认真考虑这两个,因为它们在编译时解析,并且可以为您节省方法中通常遵循的大“开关”Foo
:
void SomeOperation(int data);
void SomeOperation(float data);
// etc
变体和字节一致性
在极少数情况下,您需要操作字节本身。
本质上,变体只是包装在单个值类型(结构体)中的值类型的大联合。在 C++ 中,您可以在堆上分配值类型,因为结构与类相同(很好排序)。如何使用值类型有点重要,稍后会详细介绍。
Union 只是意味着您将重叠内存中的所有数据。请注意我是如何在上面明确指出值类型的;对于变体来说,这基本上就是它的全部内容。这也为我们提供了一种测试它的方法 - 即通过检查结构中的另一个值。
在 C# 中执行此操作的方法是使用StructLayout
值类型中的属性,其基本工作原理如下:
[StructLayout(LayoutKind.Explicit)]
public struct Variant
{
[FieldOffset(0)]
public int Integer;
[FieldOffset(0)]
public float Float;
[FieldOffset(0)]
public double Double;
[FieldOffset(0)]
public byte Byte;
// etc
}
// Check if it works - shouldn't print 0.
public class VariantTest
{
static void Main(string[] args)
{
Variant v = new Variant() { Integer = 2 };
Console.WriteLine("{0}", v.Float);
Console.ReadLine();
}
}
正如我之前提到的,C++ 变体也可以存储在堆上。如果您这样做,您可能仍然希望内存签名相同。执行此操作的方法是将我们之前构建的 Variant 结构装箱,只需将其设置为object
.