2012-04-19 17 views
11

Estoy en el medio de convertir algún código de C++/CLI a C#. Uno de los objetos tiene un destructor en la versión de C++/CLI. Algunos otros códigos C++/CLI llaman "eliminar" en este objeto después de su uso.Borrar C++/CLI de llamada en el objeto C#

¿Qué método debo implementar en la versión C# de este objeto para que esos "eliminar" sigan funcionando igual (IDisposable.Dispose, el finalizador, o algo más de lo que me falta)?

Respuesta

16

Yo diría que la interfaz IDisposable es lo que buscas si necesitas una eliminación determinista de los recursos. Este suele ser el caso de los recursos no administrados, como los identificadores no administrados que deben cerrarse, las transmisiones o las conexiones de bases de datos.

En C++/CLI, si se declara un tipo administrado (ref class y similares), IDisposable se implementa utilizando la sintaxis destructor, y Dispose() se llama mediante el uso de la palabra clave delete. Si declara localmente un objeto de este tipo gestionado (sin utilizar el operador ^ o gcnew), C++/CLI incluso llama automáticamente al Dispose() cuando el objeto queda fuera del alcance. De esa manera, C++/CLI es más conveniente que C#.

No podrá llamar al delete en el objeto al usar C#, en su lugar tendrá que llamar manualmente al Dispose(). Otra forma de deshacerse de los objetos IDisposable es el bloque using.

El finalizador (implementado en C# utilizando la sintaxis de destructor) no es lo mismo que un destructor de C++, ya que no es determinista cuando se invocará. Los objetos con un finalizador están básicamente en cola hasta que el hilo del finalizador decida llamar a su finalizador, de modo que nunca se sabe exactamente cuándo se llama.

El mejor enfoque para tratar con recursos no administrados es probablemente una combinación de los dos. Vea aquí el enfoque recomendado:
http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.100).aspx

Tenga en cuenta, sin embargo, que cuando se utiliza IDisposable, a pesar de que se puede disponer de los recursos no administrados de forma determinista, gestionan los objetos todavía tienen que ser recogidos por el recolector de basura (de manera no determinista).

Acabo de encontrar un artículo que explica las diferencias entre C++/CLI y C#. Puede encontrarlo interesante:
http://weblogs.thinktecture.com/cnagel/2006/04/ccli-finalize-and-dispose.html

+1

La semántica de la pila C++/CLI también funciona en los miembros de la clase, no solo en los locales. No hay una forma simple de duplicar esto en C#. –

+0

No lo sabía (no usa C++/CLI con mucha frecuencia). Gracias por la información :) – Botz3000

+0

Acabo de volver a leer la respuesta y hay algo que necesito aclaración: si mi clase está definida en C# (e implementa IDisposable), ¿podré llamar "eliminar" al usarla? de otra clase C++/CLI? ¡Gracias! –

0

Para mí, realmente depende de lo que esté haciendo el destructor. Si está haciendo algo así como liberar un recurso no administrado (SQL o conexiones de archivos, por ejemplo), implementaría IDispose y cerraría la conexión en el método Dispose().

Si está destruyendo otros objetos que no requieren ninguna limpieza explícita, entonces simplemente dejaría el destructor y dejaría que el GC lo manejara.

1

C# no proporciona las mismas herramientas, pero proporciona las pautas para usted: Si su dtor está cerrando una corriente, desvincular punteros, o la creación de estados apropiadamente entonces siento la interfaz IDisposable es un buen ajuste:

// C# 
void Dispose() 
{ 
    _stream.Close(); 
    _ptr = null; 
    _running = false; 
} 

// with 

obj.Dispose(); 

No se puede obligar a GC a mantener la memoria abierta. Hay formas de ayudar al GC a saber qué puede y debe liberarse, lea http://msdn.microsoft.com/en-us/library/ee787088.aspx para obtener más información.

Tenga en cuenta que al usar IDisposable se espera que llame al método Dispose() de forma adecuada. Entonces, en lugar de cada delete, querrá usar Dispose(). La parte segura de un finalizador es que cuando el GC lo esté liberando, se lo llame. Sin embargo, no puede llamar al finalizador usted mismo (por lo tanto, si el tiempo de eliminación/eliminación es importante, esa no es la solución correcta)

+1

Está convirtiendo de C++/CLI, que ya es un lenguaje administrado con recolección de basura no determinista. Pero .NET agrega un patrón de disposición determinista, que tiene un excelente soporte de lenguaje en C++/CLI, y pregunta cómo traducir eso a C#. C# también tiene soporte de idiomas para su eliminación, pero no es tan elegante. –

+0

Gracias por señalarlo, sí, tiene un patrón y lo señalé con los casos apropiados. Arreglaré mi respuesta para reflejar que proviene de C++/CLI – payo

4

Existe una falta de correspondencia entre C++/CLI y C#. El destructor C++/CLI (~ Foo) es la implementación del método IDisposable.Dispose(). Una clase C++/CLI no implementa explícitamente IDisposable, es automática solo por la presencia de un destructor.

El finalizador C++/CLI (! Foo) es el destructor C#.

Así que el C++/CLI eliminar operador es equivalente a llamar al método Dispose(). Tenga cuidado con la semántica de la pila en C++/CLI, desencadenada por una variable local de un tipo de referencia sin el^hat. El compilador genera una llamada oculta a Dispose() al final del bloque del osciloscopio. Que es equivalente a la C# usando la palabra clave. Difícil de ver desde el código fuente así que preste más atención o mire la IL generada con ildasm.exe

+1

Solo una aclaración: aunque la documentación de C# habla de "destructores", C# realmente no tiene destructores. Usar la notación de destructor en C# crea un finalizador. Y la semántica de la pila C++/CLI es mucho más poderosa que los bloques 'que usan' de C#. –