2010-08-10 11 views
5

Ich war vor kurzem durch eine Garbage Collection Artikel und beschlossen, mitzuspielen und versuchen, ein besseres Verständnis zu gewinnen. Ich codierte das Folgende, spielte mit der using Anweisung herum, war aber mit den Ergebnissen überrascht ... Ich erwartete e.Parent.Name außerhalb des Verwendungsblocks, um ka-blooyy zu gehen.C# Speicherzuweisung/Freigabe-Frage in Bezug auf Umfang

Was genau passiert hier?

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

Sie könnten in Raymond Chen letzten Blog-Artikel interessiert sein [Jeder denkt über Garbage Collection der falsche Weg] (http://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586 .aspx). –

Antwort

10

Ihre Dispose-Methode eigentlich nichts zu der Instanz von Parent tun, daher ist es immer noch fair game/arbeitet als verwendbare Instanz einer Klasse.

IDisposable wird normalerweise verwendet, wenn Ihre Klasse eine nicht verwaltete Ressource enthält, z. B. eine Datenbankverbindung oder eine Datei, sodass sie bereinigt werden kann, wenn Dispose() aufgerufen wird. Nur Dispose aufrufen tut nichts zu den nicht verwalteten Ressourcen, dort muss etwas Code in der Methode sein, die etwas zu diesen Ressourcen tut. Während C# die Syntax using() {} haben kann, um die Instanziierung und die Beseitigung eines IDisposable Objekts in einem try/catch/finally zu umbrechen, bedeutet das nicht, dass es irgendetwas "Spezielles" mit dem entsorgten Objekt tut.

Stellen Sie sich vor, hypothetisch, dass Name tatsächlich eine nicht verwaltete Ressource ist, anstatt nur einen String, könnte Ihr Dispose() Methode lesen:

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

Weil Sie p-e.Parent, das Objekt selbst ist immer noch zugewiesen haben " im Umfang "wie es einen Verweis darauf gibt, daher ist es immer noch zugänglich für Console.WriteLine(e.Parent.Name);, um die Ausgabe von zu produzieren.

Es wird auch zur Zeit „CLR Week“ über bei The Old New Thing und die ersten drei Artikel der Woche diskutieren die Garbage Collector und wie es funktioniert/verhält. Sie sind auch lesenswert:

+0

Ahh natürlich. Es passiert also nichts "Magisches", nur weil ich es in einen Benutzungsblock lege. Es ist dasselbe, als ob ich diesen Angestellten in eine Methode übergeben, die Eltern in der Methode erstellt und auf Mitarbeiter gesetzt hätte. Außerhalb der Methode kann Parent nicht vollständig erfasst werden, da auf sie immer noch verwiesen wird. Ist das korrekt? –

+0

@Mike, es ist genau das =) – Rob

1

Da e existiert immer noch in ihrem Umfang, dass irgendetwas mit e zugeordnet (die Eltern e zugewiesen) wird noch existieren bis e nicht mehr verfügbar ist.

1

IDisposable.Dispose ist für Sie gedacht, um es zu verwenden, um nicht verwaltete Ressourcen zu bereinigen, die Sie besitzen (wie Dateihandles usw.). Es macht nichts an sich. Die häufigste Verwendung ist, wenn Ihre Klasse über Membervariablen verfügt, die IDisposable selbst implementieren, und Sie jetzt für die Dispose verantwortlich sind. Es ist nur ein Muster, um Ihnen zu helfen, und hat nichts mit Garbage Collection zu tun - im Gegenteil.

1

Die Methode Dispose zerstört das Objekt nicht aus dem Speicher. Normalerweise werden durch eine Dispos-Methode nur Ressourcen freigegeben, die sie erstellt hat.

0

IDisposable ist keine Sprachfunktion und macht in der Laufzeit nichts besonderes. Es ist nur eine Schnittstelle/Methode wie jede andere. Es ist ein nützliches Muster, also haben sie die Syntax hinzugefügt, um diese Methode automatisch in einem bestimmten Muster aufzurufen (using), und es gibt spezielle Ausnahmen, die Sie mit "Dispose" in ihren Namen werfen können (wie ObjectDisposedException).

mit Blöcke drehen daraus:

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

in etwa wie folgt:

{ 
    SomeType t; 

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

Es gibt absolut keine Möglichkeit, den GC zu zwingen, etwas zu sammeln. Wenn sich Verweise auf Ihr Objekt irgendwo im Stapel befinden (ignorieren Sie unsicheren Code und C++/CLI-Code) oder wenn Sie von einem Objekt auf dem Stapel angekettete Verweise darauf haben, wird Ihr Objekt leben.

Wenn Sie, dass Code wollen die Luft zu sprengen, können Sie etwas tun:

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

Wenn Sie für ein anderes Beispiel suchen, die nach oben wird die Luft sprengen, wenn etwas zu einem angeordneten Objekt zu tun versuchen.

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(); 
     } 
    }