c# - crear - C++/CLI: prevención de la recolección de elementos no utilizados en contenedor administrado de recursos no administrados
destructor c# (2)
Tengo un NativeDog
clase C ++ no NativeDog
que debe usarse desde C #, así que he creado una clase de contenedor 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
}
}
Todo está bien, y uso la clase de esta manera:
// 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);
}
¿Ves lo que está mal? Yo tampoco lo hice al principio, hasta que cosas muy extrañas comenzaron a suceder de vez en cuando. Básicamente, si después de llamar a MyFunc()
el programa está pausado por el GC mientras se encuentra en alguna parte de la función nativa FeedDogNative
(marcada (***)
arriba), pensará que el contenedor gestionado se puede recopilar porque ya no se usará. ni en C # MyFunc (es una variable local y no se usará después de la llamada FeedDogManaged
), ni en FeedDogManaged
. Y esto en realidad sucedió en ocasiones. El GC llama al Finalizador, que delete
el objeto del perro nativo, ¡aunque FeedDogNative
no ha terminado de usarlo! Entonces mi código no administrado ahora está usando un puntero eliminado.
¿Cómo puedo prevenir esto? Puedo pensar en algunas formas (por ejemplo, una llamada falsa que pretende usar el dog
al final de FeedDogManaged
), pero ¿cuál sería la forma recomendada?
En su código administrado, agregue GC.KeepAlive(dog)
después de la llamada a FeedDogManaged ():
http://msdn.microsoft.com/en-us/library/system.gc.keepalive(VS.71).aspx
Necesita una llamada GC::KeepAlive()
en su función FeedDogManaged
. Parece que es un caso de uso exacto para eso.