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

How to add a managed value type member to an unmanaged class?

本文关键字:添加 成员 类型      更新时间:2023-10-16

我正在尝试创建一个名为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)
{
    //...
}

我不知道为什么这是不允许的。

有办法做到这一点吗?

本机类型中不能有托管数据。原因是本机类型的对象不在垃圾收集器的范围内,并且不能防止托管对象死亡。

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

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

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

c++/CLI团队最初试图允许混合类型,但事情变得复杂,最终以您现在看到的分离规则告终。有一篇很好的博客文章是关于这个的,但是我现在找不到