C#中C ++的VARIANT数据类型是什么?
我在C ++中使用VARIANT数据类型的代码。如何在C#中转换该代码?
答案 0 :(得分:8)
嗯,C ++中实际上有两个变体:boost :: variant和COM变体。解决方案或多或少地遵循相同的想法,但前者更复杂。我希望你的意思是使用后者。
首先我要说的是,如果可能的话,这是你不应该使用的东西。那就是说,你就是这样做的: - )
变种和互操作
如果需要字节表示相同,变量有时用于互操作。
如果您正在处理互操作,请务必查看MSDN上的VariantWrapper
课程并使其正常工作。
变体和移植注意事项
变体主要用于API,通常是这样的:
void Foo(SomeEnum operation, Variant data);
在C ++中这样做的原因是因为没有基类object
类,因此你需要这样的东西。移植它的最简单方法是将签名更改为:
void Foo(SomeEnum operation, object data);
但是,如果您正在移植,那么您也非常想要考虑这两个,因为它们在编译时得到解决,并且可以为您节省方法Foo
中通常遵循的大“切换”:< / p>
void SomeOperation(int data);
void SomeOperation(float data);
// etc
变体和字节一致性
在极少数情况下,您需要自己操纵字节。
本质上,变体只是一个包含在单个值类型(struct)中的值类型的大型联合。在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
。
答案 1 :(得分:1)
这是一个棘手的问题。
从C#4开始,您可以使用 dynamic 来表明该类型在运行时已知。
但是,根据我个人的理解,c ++需要在编译时知道类型。因此,您可以考虑使用object
,但C#中的object
是现有类型。
对于VARIANT的多类型,单值(AKA多态)的概念,您不需要在C#中找到相应的类型,只需定义类和接口即可。您始终可以引用一个对象作为该类实现的接口。
如果要移植代码,并找出可以在LHS中使用的语法,并且在编译时已知类型的考虑,则使用 var 。
答案 2 :(得分:1)
当 .NET实现COM接口时,只需使用VARIANT * 。
然后使用 IntPtr 类型接收指针,在.NET接收端绕过编组。
public class ComVariant
{
[StructLayout(LayoutKind.Sequential)]
public struct Variant
{
public ushort vt;
public ushort wReserved1;
public ushort wReserved2;
public ushort wReserved3;
public Int32 data01;
public Int32 data02;
}
private Variant _variant;
private IntPtr _variantPtr;
public ComVariant(int variantPtr) : this(new IntPtr(variantPtr))
{
}
public ComVariant(IntPtr variantPtr)
{
_variant = (Variant)Marshal.PtrToStructure(variantPtr, typeof(Variant));
_variantPtr = variantPtr;
}
public VarEnum Vt
{
get
{
return (VarEnum)_variant.vt;
}
set
{
_variant.vt = (ushort)value;
}
}
public object Object
{
get
{
return Marshal.GetObjectForNativeVariant(_variantPtr);
}
}
}
然后,如果您正在访问指向COM接口对象实例的VT_UNKNOWN,则只需
var variant = new ComVariant(variantPtr);
var stream = variant.Object as IStream; // will not be null if type is correct
var obj = variant.Object as IObj; // in general...
会做的伎俩,但要注意不要使用新分配的VARIANT并将其所有权授予.NET实现,而无需在某处解除分配...
对于更复杂的代码,您可以阅读 this article,其中还讨论了内存管理。
答案 3 :(得分:0)
让我们后退一步。迟早,我们想要VARIANT中的实际数据。 VARIANT只是有意义数据的持有者。假设我们将VARIANT转换为C#中的某种Object,它具有变体类型和.NET引擎下的一些原始缓冲区(例如.NET字符串可以公开原始缓冲区)。此时,需要根据对象确定VARIANT类型,并将原始数据转换或转换为变量指定的数据类型,然后创建新的Object,例如串/ INT /等。来自原始数据。
因此,不要担心将VARIANT传递给C#,而是查看变量数据类型并将其在C ++中转换为实际数据类型并将其传递给C#。
例如,如果VARIANT类型是VT_INT,那么从变量获取int并使用类似的东西:
VARIANT var;
Int^ returnInt = gcnew Int(var.intVal);
returnInt可以作为Out参数从C ++ dll中的C ++函数返回,可以从C#调用。 C ++ dll需要使用/ clr选项。
功能如下: -
void ThisFunctionReturnsAnInt(Runtime::InteropServices::OutAttribute Int^ % returnIntValue)
{
VARIANT var;
Int^ returnInt = gcnew Int(var.intVal);
}
可以对其他数据类型使用类似的方法。它是唯一自然的,VT_INT的变体实际上就像一个整数,它不像是在进行一些重大的转换,你只是把实际价值从您对它感兴趣的VARIANT就像您将C ++的直接整数值传递给C#一样。无论如何你仍然需要做gcnew。