我有一个NativeDog
需要从 C# 使用的 C++ 非托管类,所以我创建了一个包装类ManagedDog
。
// unmanaged C++ class
class NativeDog
{
NativeDog(...); // constructor
~NativeDog(); // destructor
...
}
// C++/CLI wrapper class
ref class ManagedDog
{
NativeDog* innerObject; // unmanaged, but private, won't be seen from C#
ManagedDog(...)
{
innerObject = new NativeDog(...);
...
}
~ManagedDog() // destructor (like Dispose() in C#)
{
// free unmanaged resources
if (innerObject)
delete innerObject;
}
!ManagedDog() // finalizer (like Finalize() in C#, in case
{ // the user forgets to dispose)
~ManagedDog(); // call destructor
}
}
一切都很好,我使用这样的类:
// in C++/CLI
// this function is called from C++ code
void MyLibrary::FeedDogNative(NativeDog* nativedog)
{
... // (***)
}
// this function is called from C#, passes on the dog to the native function
void MyLibrary::FeedDogManaged(ManagedDog^ dog)
{
NativeDog* rawdog = dog->innerObject;
MyLibrary::FeedDogNative(rawdog);
}
// C# client code
void MyFunc()
{
ManagedDog dog = new ManagedDog(...);
MyLibrary.FeedDogManaged(dog);
}
看看有什么问题?一开始我也没有,直到非常奇怪的事情开始不时发生。基本上,如果在调用MyFunc()
程序后被 GC 暂停,而它在本机函数中的某个位置FeedDogNative
(上面标记(***)
),它会认为托管包装器可以被收集,因为它不再被使用,在 C# MyFunc 中(它是一个本地变量并且不会在FeedDogManaged
调用后使用),在FeedDogManaged
. 所以这实际上偶尔会发生。GC 调用 Finalizer,它delete
是本机 dog 对象,即使FeedDogNative
它还没有使用完!所以我的非托管代码现在正在使用已删除的指针。
我怎样才能防止这种情况?我可以想到一些方法(例如,假装dog
在末尾使用的虚拟调用FeedDogManaged
)但是推荐的方法是什么?