c#中与c++的VARIANT数据类型等价的是什么?
我在c++中使用VARIANT数据类型的代码。我如何将代码转换为c# ?
实际上c++中有两种变体:boost::variant和COM variant。解决方案或多或少遵循相同的思想,但前者更复杂。我想你的意思是用后者。
让我首先告诉你,如果可能的话,这是你不应该使用的。也就是说,你应该这样做:-)
变量和互操作
如果您需要相同的字节表示,则有时在互操作中使用变体。
如果你正在处理互操作,请确保在MSDN上检查VariantWrapper
类并使其像那样工作。
变体和移植注意事项
变量主要用于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
变量和字节一致性
在极少数情况下,您需要操作字节本身。
本质上,该变体只是将多个值类型包装在单个值类型(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
。
When 。. 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实现而不将其释放到某个地方…
对于更复杂的代码,您可以阅读这篇文章,它也讨论了内存管理。
这是一个棘手的问题。
从c# 4开始,可以使用dynamic来表示该类型在运行时是已知的。
根据我个人的理解,c++需要编译时已知的类型。因此,您可能会考虑使用object
,但object
在c#中是一个存在的类型。
对于多类型、单值(又名多态性)的概念,您不需要在c#中找到相应的类型,只需定义您的类和接口。你总是可以引用一个对象作为类实现的接口。
如果您正在移植代码,并且要找出可以简单地在LHS中使用的语法,并且考虑到编译时已知的类型,则使用var
让我们回顾一下。迟早,我们需要在VARIANT中获得实际数据。变体只是有意义的数据的保存器。假设我们将VARIANT转换为c#中的某种对象,该对象具有。net下的变体类型和一些原始缓冲区(例如。net字符串可以公开原始缓冲区)。此时,VARIANT类型需要从对象中确定,并将原始数据转换或强制转换为变体指定的数据类型,然后创建一个新对象,例如string/int/等。从原始数据
因此,与其担心将VARIANT传递给c#,不如查看变量数据类型并将其在c++中转换为实际数据类型并将其传递给c#。
例如,如果VARIANT类型是VT_INT,那么从变体中获取int,并可以使用如下内容:
VARIANT var;
Int^ returnInt = gcnew Int(var.intVal);
return可以作为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的一个变体就像一个int,它不像是有一些主要的转换在进行,你只是在你感兴趣的时候从变体中取出实际的值,就像你从c++传递一个直接的整数值到c#一样。无论如何,您仍然需要执行gcnew。