如何将托管值类型成员添加到非托管class?
How to add a managed value type member to an unmanaged class?
我正在尝试创建一个名为 MyNativeClass
的本机 C++ class,它可以被非托管代码使用。 MyNativeClass
的成员函数是使用托管代码实现的。此外,托管代码需要一个 System::Numerics::BigInteger
对象,但是当我尝试将 System::Numerics::BigInteger bi_
字段添加到 MyNativeClass
时,我得到
error C3265: cannot declare a managed 'bi_' in an unmanaged 'MyNativeClass'
这是一个简化的代码清单,它演示了我正在努力实现的目标:
mynativeclass.h
class MyNativeClass
{
//...
public:
MyNativeClass();
//...
private:
System::Numerics::BigInteger bi_;
//...
};
mynativeclass.cc
MyNativeClass::MyNativeClass()
: bi_(BigInteger::Zero)
{
//...
}
我不确定为什么不允许这样做。
有办法吗?
您不能在本机类型中包含托管数据。原因是本机类型的对象在垃圾收集器的范围之外,并且不会阻止托管对象死亡。
"I know that",你说。 "But value types aren't kept on the managed heap and don't need to have their lifetime tracked by the garbage collector!" 没错。但是托管值类型可能包含引用类型的句柄。如果垃圾收集器看不到它们,它就无法保持它们的引用(或在分代垃圾收集器压缩堆/将对象提升到更高的世代时调整它们)。
可以将可 blittable 数据(不包含句柄)直接存储在本机内存中。事实上,这对于具有双重标识的原始类型是允许的(例如,原生 int
== System.Int32
)。但它不允许用于任何复合类型,大概是为了保持语言规则简单。而且 BitInteger
无论如何都不会被允许,因为它确实需要保留一个可变大小的内容区域的句柄(使用 dotPeek 或参考源,它显示为 array<unsigned>^
)以便支持任意精度。
解决方法是使用垃圾收集器的 GCHandle
功能,使垃圾收集器范围之外的对象保持活动状态。但是用 GCHandles 替换值 class 内的所有句柄会导致不兼容的内存布局,因此它实际上不再是相同的类型。最简单的解决方案是使用 gcroot<>
(这是 GCHandle
的一个很好的 C++/CLI 接口)引用 class 类型,并将您的托管值类型粘贴在其中。
C++/CLI 团队最初尝试允许混合类型,但事情变得复杂并以您现在看到的分离规则告终。有一篇关于它的相当不错的博客文章,但我现在找不到了。
我正在尝试创建一个名为 MyNativeClass
的本机 C++ class,它可以被非托管代码使用。 MyNativeClass
的成员函数是使用托管代码实现的。此外,托管代码需要一个 System::Numerics::BigInteger
对象,但是当我尝试将 System::Numerics::BigInteger bi_
字段添加到 MyNativeClass
时,我得到
error C3265: cannot declare a managed 'bi_' in an unmanaged 'MyNativeClass'
这是一个简化的代码清单,它演示了我正在努力实现的目标:
mynativeclass.h
class MyNativeClass
{
//...
public:
MyNativeClass();
//...
private:
System::Numerics::BigInteger bi_;
//...
};
mynativeclass.cc
MyNativeClass::MyNativeClass()
: bi_(BigInteger::Zero)
{
//...
}
我不确定为什么不允许这样做。
有办法吗?
您不能在本机类型中包含托管数据。原因是本机类型的对象在垃圾收集器的范围之外,并且不会阻止托管对象死亡。
"I know that",你说。 "But value types aren't kept on the managed heap and don't need to have their lifetime tracked by the garbage collector!" 没错。但是托管值类型可能包含引用类型的句柄。如果垃圾收集器看不到它们,它就无法保持它们的引用(或在分代垃圾收集器压缩堆/将对象提升到更高的世代时调整它们)。
可以将可 blittable 数据(不包含句柄)直接存储在本机内存中。事实上,这对于具有双重标识的原始类型是允许的(例如,原生 int
== System.Int32
)。但它不允许用于任何复合类型,大概是为了保持语言规则简单。而且 BitInteger
无论如何都不会被允许,因为它确实需要保留一个可变大小的内容区域的句柄(使用 dotPeek 或参考源,它显示为 array<unsigned>^
)以便支持任意精度。
解决方法是使用垃圾收集器的 GCHandle
功能,使垃圾收集器范围之外的对象保持活动状态。但是用 GCHandles 替换值 class 内的所有句柄会导致不兼容的内存布局,因此它实际上不再是相同的类型。最简单的解决方案是使用 gcroot<>
(这是 GCHandle
的一个很好的 C++/CLI 接口)引用 class 类型,并将您的托管值类型粘贴在其中。
C++/CLI 团队最初尝试允许混合类型,但事情变得复杂并以您现在看到的分离规则告终。有一篇关于它的相当不错的博客文章,但我现在找不到了。