2010-08-10 17 views
5

Hace poco estuve revisando un artículo sobre recolección de basura y decidí seguirlo y tratar de obtener una mayor comprensión. Codifiqué lo siguiente, jugando con la declaración using, pero me sorprendieron los resultados ... Esperaba que e.Parent.Name fuera del bloque de uso fuera ka-blooey.C# Pregunta asignación/desasignación de memoria con respecto al alcance

¿Qué está pasando exactamente aquí?

static void Main(string[] args) 
     { 
      Employee e = new Employee(); 

      using (Parent p = new Parent()) 
      { 
       p.Name = "Betsy"; 
       e.Parent = p; 
       Console.WriteLine(e.Parent.Name); 
      } 

      Console.WriteLine(e.Parent.Name);    

      Console.ReadLine(); 
     } 

     public class Employee 
     { 
      public Parent Parent; 
     } 

     public class Parent : IDisposable 
     { 
      public string Name; 

      public void Dispose() 
      { 
       Console.WriteLine("Disposing Parent"); 
      } 
     } 
+2

Usted puede estar interesado en el artículo reciente del blog de Raymond Chen [Todo el mundo piensa acerca de la recolección de basura por el camino equivocado] (http://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586 .aspx). –

Respuesta

10

Su método Dispose no hace nada a la instancia de Parent, por lo tanto, sigue siendo un juego justo/funciona como un ejemplo útil de una clase.

IDisposable se utiliza generalmente cuando su clase se aferra a un recurso no administrado, como una conexión de base de datos o un archivo, para que se pueda limpiar cuando se llame Dispose(). Simplemente llamando al Dispose no le hace nada a los recursos no administrados, allí tiene que haber algún código en el método que haga algo con esos recursos. Mientras que C# podría tener la sintaxis using() {} para envolver la instanciación y eliminación de un objeto IDisposable en try/catch/finally, no significa que haga algo "especial" con el objeto eliminado.

Imagínese, hipotéticamente, que Name es en realidad un recurso no administrado, en lugar de sólo una cadena, su método de Dispose() podía leer:

public void Dispose() 
{ 
    Name = null; 
    Console.WriteLine("Disposing Parent"); 
} 

Debido a que ha asignado p-e.Parent, el objeto en sí sigue siendo " en el alcance "ya que hay una referencia al mismo, por lo tanto, todavía está disponible para Console.WriteLine(e.Parent.Name); para generar resultados.

Actualmente también es "Semana CLR" en The Old New Thing y los primeros 3 artículos de la semana están discutiendo el recolector de basura y cómo funciona/se comporta. Están bien vale la pena leer:

+0

Ahh, por supuesto. Entonces, nada 'mágico' está sucediendo solo porque lo puse en un bloque de uso. Es lo mismo que si hubiera pasado a ese Empleado en un método, haya creado el Padre en el método y lo haya configurado para el Empleado. Fuera del método, el padre no se puede cobrar totalmente porque todavía se hace referencia a él. ¿Es eso correcto? –

+0

@Mike, es exactamente eso =) – Rob

1

Desde e todavía existe en su alcance que cualquier cosa asociada con el correo (el padre asignado a e) seguirá existiendo hasta que e esté fuera del alcance.

1

IDisposable.Dispose está destinado para que lo use para limpiar los recursos no administrados que posee (como manejadores de archivos, etc.) No hace nada por derecho propio. El uso más común es si su clase tenía variables miembro que implementan IDisposable, usted ahora es responsable de deshacerse de ellas. Es solo un patrón para ayudarlo, y no tiene nada que ver con Garbage Collection, sino todo lo contrario.

1

El método Dispose no destruye el objeto de la memoria. Normalmente, un método de eliminación solo liberará los recursos que creó.

0

IDisposable no es una función de idioma, y ​​no tiene nada de especial en el tiempo de ejecución. Es solo una interfaz/método como cualquier otro. Resulta ser un patrón útil, por lo que agregaron sintaxis para llamar automáticamente a ese método en un patrón específico (using), y hay excepciones especiales que puede lanzar que tienen "Eliminar" en su nombre (como ObjectDisposedException).

utilizando bloques a su vez de esto:

using(SomeType t = new SomeType()) 
{ 
    t.Something(); 
} 

en algo como esto:

{ 
    SomeType t; 

    try 
    { 
    t = new SomeType(); 
    t.Something(); 
    } 
    finally 
    { 
    t.Dispose(); 
    } 
} 

no hay absolutamente ninguna manera de forzar a la GC para recoger cualquier cosa. Si hay referencias a su objeto en algún lugar de la pila (ignorando el código inseguro y C++/CLI), o referencias encadenadas a él desde algún objeto en la pila, entonces su objeto vivirá.

Si desea que el código para hacer estallar, se puede hacer algo como esto:

public class Parent : IDisposable 
{ 
    public string Name 
    { 
     get 
     { 
      AssertNotDisposed(); 
      return name; 
     } 
     set 
     { 
      AssertNotDisposed(); 
      name = value; 
     } 
    } 

    public void Dispose() 
    { 
     AssertNotDisposed(); 
     Console.WriteLine("Disposing Parent"); 
     isDisposed = true; 
    } 

    private void AssertNotDisposed() 
    { 
     if(isDisposed) 
      throw new ObjectDisposedException("some message"); 
    } 

    private string name; 
    private bool isDisposed = false; 
} 
0

Si lo que buscas es otro ejemplo que estallar cuando se trata de hacer algo a un objeto desechado.

static void Main(string[] args) 
     { 
     Employee e = new Employee(); 

     using (Parent p = new Parent("test.txt")) 
     { 

      e.Parent = p; 



      using (System.IO.StreamWriter fileWriter = 
       new System.IO.StreamWriter(e.Parent.File)) 
       { 
       fileWriter.WriteLine("Betsy"); 
      } 

     } 



    using (System.IO.StreamWriter fileWriter = 
      new System.IO.StreamWriter(e.Parent.File)) 
     { 
      fileWriter.WriteLine("Betsy"); //uh-oh 
     } 


     Console.ReadLine(); 
    } 


    public class Employee 
    { 
     public Parent Parent; 
    } 

    public class Parent : IDisposable 
    { 
     public System.IO.FileStream File; 

     public Parent(string fileName) 
     { 
      File = System.IO.File.Open(fileName, System.IO.FileMode.OpenOrCreate); 

     } 

     public void Dispose() 
     { 
      ((IDisposable)File).Dispose(); //or File.Close(); 
     } 
    } 
Cuestiones relacionadas