¿Puedo detectar si un objeto ha llamado a GC.SuppressFinalize?

¿Hay alguna forma de detectar si un objeto ha llamado a GC.SuppressFinalize?

Tengo un objeto que se parece a esto (el patrón Dispose completo se eliminó para mayor claridad):

public class ResourceWrapper {
    private readonly bool _ownsResource;
    private readonly UnmanagedResource _resource;

    public ResourceWrapper(UnmanagedResource resource, bool ownsResource) {
        _resource = resource;
        _ownsResource = ownsResource;
        if (!ownsResource)
            GC.SuppressFinalize(this);
    }
    ~ResourceWrapper() {
        if (_ownsResource)
            // clean up the unmanaged resource
    }
}

Si la línea ownsResource el parámetro del constructor es false, entonces el finalizador no tendrá nada que hacer, por lo que parece razonable (aunque un poco peculiar) llamar GC.SuppressFinalize directamente desde el constructor. Sin embargo, debido a que este comportamiento es peculiar, estoy muy tentado de anotarlo en un comentario de documento XML ... y si tengo la tentación de comentarlo, entonces debería escribir una prueba unitaria para ello.

Pero mientras System.GC tiene métodos para conjunto la finalizabilidad de un objetoSuppressFinalize, ReRegisterForFinalize), No veo ningún método para obtener la finalizabilidad de un objeto. ¿Hay alguna forma de consultar si se ha llamado a GC.SuppressFinalize en una instancia determinada, además de comprar Typemock o escribir mi propio host CLR?

preguntado el 08 de enero de 11 a las 15:01

3 Respuestas

Si desea confirmar que la finalización se ha suprimido si su objeto no es el propietario del recurso, ¿quizás podría hacer que el finalizador afirme que es el propietario del recurso? La prueba tendría que hacer GC.Collect y GC.WaitForPendingFinalizers, pero el código de producción no tendría nada adicional excepto la aserción (que podría omitirse de la compilación de producción). Una pequeña advertencia con la aserción: si el hilo que crea el objeto muere entre la creación del objeto y el establecimiento del estado de propiedad, el finalizador puede ejecutarse de manera inapropiada.

Habiendo dicho eso, me pregunto si sería mejor tener un tipo ResourceWrapper abstracto, con subtipos separados OwnResourceWrapper y SharedResourceWrapper, que posean o no el recurso en cuestión. Entonces, el subtipo que no posee recursos no necesitaría tener un finalizador en primer lugar. Tenga en cuenta que puede ser útil para SharedResourceWrapper implementar IDisposable como no-op.

Respondido el 14 de enero de 11 a las 18:01

Ambas buenas ideas, pero me gusta la segunda: trasladar la responsabilidad del "propietario" a un objeto separado. - Joe White

Usar un tipo de objeto separado es IMHO apto para ser más limpio cuando funciona (es decir, el contenedor sabe cuando se crea si el recurso será propiedad, y ese estado nunca cambiará) pero puede requerir un poco más de código. Sin duda, es útil tener ambas técnicas disponibles. Como se señaló en la edición anterior, los finalizadores se ejecutan de forma predeterminada antes de que se establezca, habrá algo que puedan hacer; eso parece un poco descuidado, pero probablemente no será un problema en la mayoría de los escenarios. - Super gato

Esto no es posible, el GC simplemente no proporciona esta información. Una buena razón para ello, no son solo dos estados en los que el objeto puede estar. También puede que ya esté en la cola de finalización o que ya se haya finalizado.

Un host CLR personalizado no lo ayudará con eso, la interfaz de hospedaje no proporciona ningún enlace al gc. Puede comprobar si se ha llamado a SuppressFinalize cuando debería hacerlo simplemente marcando esto en el finalizador. Regístrese (rápidamente). No puedes probar lo contrario.

Fwiw, las clases de marcos .NET no hacen esto, simplemente dejan que el finalizador se ejecute de todos modos.

Respondido el 08 de enero de 11 a las 19:01

Algunas clases de BCL realmente hacen SuppressFinalize desde sus constructores, por ejemplo, SqlConnection (aunque no he visto ninguna que lo haga de forma condicional). - Joe White

Esto puede ayudar (reductio absurdum). Un truco sería hacer un registro (esto podría ser un estado estático) en los finalizadores y si alguien está ausente, ha llamado a la supresión de finalización, pero aún no puede estar seguro de cuándo.

Esto funciona si es el autor del tipo.

Respondido el 08 de enero de 11 a las 19:01

Pensé en eso. Eso también requeriría llamar a GC.Collect y GC.WaitForPendingFinalizers desde mi prueba, aunque eso es factible. Pero odio hacer que el código de producción haga un trabajo adicional que sea solo para pruebas. - Joe White

No lo recomiendo en absoluto, pero sigue siendo una solución. Tal vez encuentre una solución más confiable de otros. - Xaqron

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.