2011-09-17 7 views
11

¿Es legal llamar a un método sobre un objeto desechado? ¿Si es así por qué?¿Por qué un objeto eliminado no arroja una excepción al usarlo después de eliminarlo?

En el siguiente programa de demostración, tengo una clase desechable A (que implementa IDisposable interfaz) .AS lo que sé, si paso objeto desechable para using() constructo, a continuación, Dispose() método obtiene llamado automáticamente en el corchete de cierre:

A a = new A(); 
using (a) 
{ 
    //... 
}//<--------- a.Dispose() gets called here! 

//here the object is supposed to be disposed, 
//and shouldn't be used, as far as I understand. 

Si esto es correcto, entonces por favor explicar la salida de este programa:

public class A : IDisposable 
{ 
    int i = 100; 
    public void Dispose() 
    { 
     Console.WriteLine("Dispose() called"); 
    } 
    public void f() 
    { 
     Console.WriteLine("{0}", i); i *= 2; 
    } 
} 

public class Test 
{ 
     public static void Main() 
     { 
       A a = new A(); 
       Console.WriteLine("Before using()"); 
       a.f(); 
       using (a) 
       { 
        Console.WriteLine("Inside using()"); 
        a.f(); 
       } 
       Console.WriteLine("After using()"); 
       a.f(); 
     } 
} 

salida (ideone):

Before using() 
100 
Inside using() 
200 
Dispose() called 
After using() 
400 

¿Cómo puedo llamar f() en el objeto desechado a? Está permitido? Si es así, ¿por qué? Si no, ¿por qué el programa anterior no da excepción en tiempo de ejecución?


sé que la construcción popular de usar using es la siguiente:

using (A a = new A()) 
{ 
    //working with a 
} 

Pero yo sólo estoy experimentando, es por eso que he escrito de manera diferente.

+3

Veo que a alguien le falta la naturaleza determinista de la gestión de la memoria en C++. :) – ChaosPandion

+7

Entonces, lo que estás diciendo es: escribí un programa que no implementa el contrato de objetos desechables, y cuando lo ejecuto, no implementa el contrato de objetos desechables. ** Usted ** es responsable de implementar ese comportamiento. No lo hiciste Ve hazlo. –

Respuesta

11

Disposed no significa que se haya ido. Dispuesto solo significa que se ha liberado cualquier recurso no administrado (como un archivo, conexión de cualquier tipo, ...). Si bien esto generalmente significa que el objeto no proporciona ninguna funcionalidad útil, aún puede haber métodos que no dependan de ese recurso no administrado y sigan funcionando como siempre.

El mecanismo de eliminación existe como .net (y heredadamente, C# .net) es un entorno recogido de basura, lo que significa que usted no es responsable de la gestión de la memoria. Sin embargo, el recolector de basura no puede decidir si se ha terminado de usar un recurso no administrado, por lo que debe hacerlo usted mismo.

Si quieres métodos para lanzar una excepción después de que el objeto ha sido eliminara, tendrá un valor lógico para capturar el estado de disponer, y una vez que está dispuesto el objeto, se lanza la excepción:

public class A : IDisposable 
{ 
    int i = 100; 
    bool disposed = false; 
    public void Dispose() 
    { 
     disposed = true; 
     Console.WriteLine("Dispose() called"); 
    } 
    public void f() 
    { 
     if(disposed) 
     throw new ObjectDisposedException(); 

     Console.WriteLine("{0}", i); i *= 2; 
    } 
} 
+0

Disposed * debe * significa que se libera cualquier recurso no administrado. Pero no tiene por qué hacerlo, si la clase está mal implementada. – svick

+0

Obviamente. Pero ese es el caso con cualquier comportamiento crítico, asumí el caso ideal. – Femaref

+0

Pero según el patrón Dispose dispose significa que los recursos administrados y no administrados se liberan cuando el cliente los llama externamente. En ese caso, espero que ningún método funcione. ¿derecho? – YakRangi

6

La excepción no se produce porque no ha diseñado los métodos para arrojar ObjectDisposedException después de haber llamado al Dispose.

El clr no sabe automágicamente que debe arrojar ObjectDisposedException una vez que se llama a Dispose. Es su responsabilidad lanzar una excepción si Dispose ha liberado los recursos necesarios para la ejecución exitosa de sus métodos.

2

Un triturador en C# no es lo mismo que un destructor en C++. Un triturador se utiliza para liberar recursos administrados (o no administrados) mientras el objeto sigue siendo válido.

Las excepciones se lanzan dependiendo de la implementación de la clase. Si f() no requiere el uso de sus objetos ya dispuestos, entonces no necesariamente es necesario lanzar una excepción.

3

Una implementación típica de Dispose() solo llama a Dispose() en cualquier objeto que almacena en sus campos que son desechables. Que a su vez libera recursos no administrados.Si implementa IDisposable y no hace nada, como lo hizo en su fragmento, entonces el estado del objeto no cambia en absoluto. Nada puede salir mal No mezcle la eliminación con la finalización.

2

Calling Dispose() no establece la referencia del objeto en nulo, y su clase desechable personalizada no contiene ninguna lógica para lanzar una excepción si se accede a sus funciones después de haber llamado a Dispose(), por supuesto legal.

En el mundo real, Dispose() libera recursos no administrados y esos recursos no estarán disponibles a partir de entonces, y/o el autor de la clase lo arrojará ObjectDisposedException si intenta utilizar el objeto después de llamar a Dispose(). Normalmente, un valor booleano de nivel de clase se establece en verdadero en el cuerpo de Dispose() y ese valor se comprueba en los otros miembros de la clase antes de que realicen algún trabajo, con la excepción de que se lanza si el bool es verdadero.

2

El objetivo de IDisposable es permitir que un objeto fije el estado de cualquier entidad externa que, para su beneficio, se haya puesto en un estado que no sea ideal para otros fines. Por ejemplo, un objeto Io.Ports.SerialPort podría haber cambiado el estado de un puerto serie de "disponible para cualquier aplicación que lo desee" a "solo utilizable por un objeto Io.Ports.SerialPort particular"; el objetivo principal de SerialPort.Dispose es restaurar el estado del puerto serie a "disponible para cualquier aplicación".

Por supuesto, una vez que un objeto que implementa IDisposable ha restablecido las entidades que han estado manteniendo un cierto estado para su beneficio, ya no tendrá el beneficio del estado mantenido de esas entidades. Por ejemplo, una vez que el estado del puerto serie se ha establecido en "disponible para cualquier aplicación", las secuencias de datos con las que se ha asociado ya no se pueden usar para enviar y recibir datos. Si un objeto puede funcionar normalmente sin que las entidades externas se pongan en un estado especial para su beneficio, no habría ninguna razón para dejar a las entidades externas en un estado especial en primer lugar.

Generalmente, después de que IDisposable.Dispose ha sido llamado en un objeto, no se debe esperar que el objeto sea capaz de hacer mucho. Intentar usar la mayoría de los métodos en un objeto así indicaría un error; si no se puede esperar razonablemente que un método funcione, la forma correcta de indicarlo es mediante ObjectDisposedException.

Microsoft sugiere que casi todos los métodos en un objeto que implementa IDisposable deben arrojar ObjectDisposedException si se usan en un objeto que ha sido eliminado. Sugeriría que tal consejo sea demasiado amplio. A menudo es muy útil para los dispositivos exponer métodos o propiedades para descubrir qué sucedió mientras el objeto estaba vivo. Aunque se podría dar a una clase de comunicaciones un método Close así como un método Dispose, y solo permitir a uno consultar cosas como NumberOfPacketsExchanged después de un cierre pero no después de un Dispose, pero eso parece excesivamente complicado. Leer propiedades relacionadas con cosas que sucedieron antes de que un objeto fuera Dispuesto parece un patrón perfectamente razonable.

Cuestiones relacionadas