我有一个C非管理类NativeDog需要从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,它会删除本机狗对象,即使FeedDogNative尚未完成使用!所以我的非托管代码现在使用一个已删除的指针.
我该如何防止这种情况?我可以想到一些方法(例如,在FeedDogManaged结束时假装使用狗的虚拟呼叫),但是推荐的方法是什么?