2009-04-12 5 views
3

Ich bin neugierig auf die tatsächliche .NET-Implementierung und die Entscheidung dahinter.Wie erfassten Wert in anonymen Methoden in .NET implementiert werden

Zum Beispiel müssen in Java alle erfassten Werte, die in anonymen Klassen verwendet werden, endgültig sein. Diese Anforderung scheint in .NET gelöscht zu werden.

Gibt es auch einen Unterschied in einer Implementierung von erfassten Werten für Werttypen im Gegensatz zu Referenztypen?

Dank

Antwort

8

Der einfachste Weg zu finden, wie es umgesetzt ist, ist es zu versuchen. Schreiben Sie einen Code, der eine erfasste Variable verwendet, kompilieren Sie sie und betrachten Sie sie dann in Reflector. Beachten Sie, dass es die Variable ist, die erfasst wird, nicht die Wert. Das ist einer der großen Unterschiede zwischen Java und C# in diesem Bereich.

Die Grundidee ist, dass jede Ebene des Bereichs, die mindestens eine erfasste Variable enthält, zu einer neuen Klasse mit Feldern für die Variablen führt, die erfasst wurden. Wenn es mehr als eine Ebene gibt, hat ein innerer Bereich auch ein Feld für den nächsten Bereich und so weiter. Die echten lokalen Variablen im Stapel sind Verweise auf Instanzen der automatisch generierten Klassen.

Hier ist ein Beispiel:

using System; 
using System.Collections.Generic; 

class Program 
{ 
    static void Main() 
    { 
     List<Action> actions = new List<Action>(); 

     for (int i=0; i < 5; i++) 
     { 
      int copyOfI = i; 

      for (int j=0; j < 5; j++) 
      { 
       int copyOfJ = j; 

       actions.Add(delegate 
       { 
        Console.WriteLine("{0} {1}", copyOfI, copyOfJ); 
       }); 
      } 
     } 

     foreach (Action action in actions) 
     { 
      action(); 
     }   
    } 
} 

(Sie unterschiedliche Ergebnisse erhalten, wenn Sie nicht über eine Kopie natürlich nehmen - Experiment!) Diese in Code wie folgt zusammengestellt:

using System; 
using System.Collections.Generic; 

class Program 
{ 
    static void Main() 
    { 
     List<Action> actions = new List<Action>(); 

     for (int i=0; i < 5; i++) 
     { 
      OuterScope outer = new OuterScope(); 
      outer.copyOfI = i; 

      for (int j=0; j < 5; j++) 
      { 
       InnerScope inner = new InnerScope(); 
       inner.outer = outer; 
       inner.copyOfJ = j; 

       actions.Add(inner.Action); 
      } 
     } 

     foreach (Action action in actions) 
     { 
      action(); 
     }   
    } 

    class OuterScope 
    { 
     public int copyOfI; 
    } 

    class InnerScope 
    { 
     public int copyOfJ; 
     public OuterScope outer; 

     public void Action() 
     { 
      Console.WriteLine("{0} {1}", outer.copyOfI, copyOfJ); 
     } 
    } 
} 

Jeder Verweis auf die erfasste Variable endet durch die Instanz der generierten Klasse, also ist es nicht nur eine einmalige Kopie. (Okay, in diesem Fall verwendet nichts anderes im Code die erfassten Variablen, aber Sie können es sich leicht vorstellen.) Beachten Sie, dass für jede Iteration der äußeren Schleife die fünf neuen Instanzen alle eine Instanz von OuterScope gemeinsam haben. Sie können versuchen, mit zusätzlichen Code im Delegaten zu spielen, um zu sehen, wie sich das auf Dinge auswirkt - wenn der Delegat copyofI ändert, wird diese Änderung im nächsten Delegaten angezeigt; Änderungen an werden nicht angezeigt, da der nächste Delegat eine separate Instanz InnerScope verwendet.

+0

Hallo, sehr schöne Erklärung. Sie können auch anzeigen, was der Compiler ohne das Kopieren auf lokale Variable produzieren würde. –

Verwandte Themen