如何将托管值类型成员添加到非托管类?

时间:2015-09-21 21:36:57

标签: c++ c++-cli

我正在尝试创建一个名为MyNativeClass的本机C ++类,可以由非托管代码使用。 MyNativeClass的成员函数使用托管代码实现。此外,托管代码需要System::Numerics::BigInteger个对象,但当我尝试向System::Numerics::BigInteger bi_添加MyNativeClass字段时,我会

  

错误C3265:无法在非托管“MyNativeClass”中声明托管“bi_”

这是一个简化的代码清单,演示了我想要实现的目标:

mynativeclass.h

class MyNativeClass
{
    //...

public:
    MyNativeClass();
    //...

private:
    System::Numerics::BigInteger bi_;

    //...
};

mynativeclass.cc

MyNativeClass::MyNativeClass()
    : bi_(BigInteger::Zero)
{
    //...
}

我不确定为什么不允许这样做。

有办法做到这一点吗?

1 个答案:

答案 0 :(得分:3)

您无法在本机类型中拥有托管数据。原因是本机类型的对象不在垃圾收集器的范围内,也不会使托管对象不再死亡。

“我知道”,你说。 “但是值类型不会保留在托管堆上,也不需要让垃圾收集器跟踪它们的生命周期!”很正确。但是托管值类型可能包含引用类型的句柄。如果垃圾收集器无法看到它们,它就无法保持它们的指示对象存活(或者当世代垃圾收集器压缩堆/将对象提升到更高代时)进行调整。

可以将blittable数据(不包含句柄)直接存储在本机内存中。事实上,这允许原始类型,它具有双重标识(例如,本机int == System.Int32)。但它不允许任何复合类型,大概是为了保持语言规则简单。并且BitInteger无论如何都不会被允许,因为它确实需要保持对可变大小内容区域的句柄(使用dotPeek或参考源,它被显示为array<unsigned>^)支持任意精度。

解决方法是使用垃圾收集器的GCHandle功能,使对象保持在垃圾收集器领域之外。但是用GCHandles替换值类中的所有句柄会导致内存布局不兼容,因此它实际上不再是同一类型。最简单的解决方案是使用gcroot<>(这是GCHandle的一个很好的C ++ / CLI接口)到引用类类型,并将托管值类型粘贴到其中。

C ++ / CLI团队最初尝试允许混合类型,但事情变得复杂并最终得到了您现在看到的分离规则。有一篇相当不错的博客文章,但我现在找不到它。