将C ++的VARIANT数据类型转换为C#

问题描述 投票:9回答:4

什么相当于C#中的C ++ VARIANT数据类型?

我在C ++中使用VARIANT数据类型的代码。如何在C#中转换该代码?

c# c++ type-conversion variant
4个回答
1
投票

这是一个棘手的问题。

从C#4开始,您可以使用dynamic来指示在运行时已知类型。

但是,根据我个人的理解,c ++需要在编译时知道类型。因此,您可能会考虑使用object,但C#中的object是一种存在类型。

对于VARIANT的多类型,单值(AKA多态)的概念,您不需要在C#中找到相应的类型,只需定义类和接口即可。您始终可以引用一个对象作为该类实现的接口。

如果要移植代码,并找出可以在LHS中使用的语法,并且在编译时已知类型的考虑,则使用var。


8
投票

好吧,C ++中实际上有两个变体:boost :: variant和COM变体。解决方案或多或少地遵循相同的想法,但前者更复杂。我希望你的意思是使用后者。

首先让我先告诉我,如果可能的话,这是你不应该使用的东西。那就是说,你就是这样做的:-)

变种和互操作

如果您需要字节表示相同,变量有时用于互操作。

如果您正在处理互操作,请务必查看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 ++变体也可以存储在堆上。如果这样做,您可能仍希望内存签名相同。这样做的方法是通过简单地将它封装到object来封装我们之前构建的Variant结构。


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,它也讨论了内存管理。


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就像一个int,它不像是有一些重大的转换,你只是在你对它感兴趣的时候从VARIANT中取出实际值如果你将一个直的整数值从C ++传递给C#。无论如何你仍然需要做gcnew。

© www.soinside.com 2019 - 2024. All rights reserved.