У меня есть неуправляемый класс С++ 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
(помечена (***)
выше), она будет думать, что управляемая обертка может быть собрана, потому что она больше не будет использоваться, ни в С# MyFunc (это локальная переменная и не будет использоваться после вызова FeedDogManaged
), ни в FeedDogManaged
. И это на самом деле произошло иногда. GC вызывает Finalizer, который delete
является родным объектом собаки, хотя FeedDogNative
не закончил его использовать! Поэтому мой неуправляемый код теперь использует удаленный указатель.
Как я могу это предотвратить? Я могу думать о некоторых способах (например, фиктивный вызов притворяется, что он использует dog
в конце FeedDogManaged
), но каков был бы рекомендуемый способ?