我正在尝试创建一个名为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)
{
//...
}
我不确定为什么不允许这样做。
有办法做到这一点吗?
答案 0 :(得分:3)
您无法在本机类型中拥有托管数据。原因是本机类型的对象不在垃圾收集器的范围内,也不会使托管对象不再死亡。
“我知道”,你说。 “但是值类型不会保留在托管堆上,也不需要让垃圾收集器跟踪它们的生命周期!”很正确。但是托管值类型可能包含引用类型的句柄。如果垃圾收集器无法看到它们,它就无法保持它们的指示对象存活(或者当世代垃圾收集器压缩堆/将对象提升到更高代时)进行调整。
可以将blittable数据(不包含句柄)直接存储在本机内存中。事实上,这允许原始类型,它具有双重标识(例如,本机int
== System.Int32
)。但它不允许任何复合类型,大概是为了保持语言规则简单。并且BitInteger
无论如何都不会被允许,因为它确实需要保持对可变大小内容区域的句柄(使用dotPeek或参考源,它被显示为array<unsigned>^
)支持任意精度。
解决方法是使用垃圾收集器的GCHandle
功能,使对象保持在垃圾收集器领域之外。但是用GCHandles替换值类中的所有句柄会导致内存布局不兼容,因此它实际上不再是同一类型。最简单的解决方案是使用gcroot<>
(这是GCHandle
的一个很好的C ++ / CLI接口)到引用类类型,并将托管值类型粘贴到其中。
C ++ / CLI团队最初尝试允许混合类型,但事情变得复杂并最终得到了您现在看到的分离规则。有一篇相当不错的博客文章,但我现在找不到它。