C ++ / CLI:防止非托管资源的托管包装上的垃圾回收

我有一个需要在C#中使用的C ++非托管类NativeDog ,所以我创建了一个包装类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本机狗对象,即使FeedDogNative尚未使用它! 所以我的非托管代码现在使用已删除的指针。

我怎么能阻止这个? 我可以想到一些方法(例如假装在FeedDogManaged结束时使用dog的虚拟调用)但推荐的方式是什么?

您需要在FeedDogManaged函数中调用GC::KeepAlive() 。 似乎它是一个确切的用例。

在托管代码中,在调用FeedDogManaged()之后添加GC.KeepAlive(dog) ):

http://msdn.microsoft.com/en-us/library/system.gc.keepalive(VS.71).aspx