2016-07-28 14 views
0

Ich erstelle eine anonyme Methode und übertrage sie in eine Aktion, die später aufgerufen wird. Ich möchte einige numerische Daten (int) in meine anonyme Methode übergeben. Um die Daten nach Wert zu übergeben, muss ich Kopien erstellen? Oder werden die Daten nach Wert weitergegeben?Erhalten anonyme Methoden, die an Aktionen übergeben werden, Daten nach Wert oder Verweis?

Hier ist, was ich denke, die Umsetzung aussehen würde, wenn ich Kopien erstellen habe:

private void CreateAction() 
{ 
    int bus = 4; 
    CustomObject[] data = new object[16]; 
    int length = 1500; 

    this.doWorkLater = new Action(() => 
    { 
     var busCopy = bus; 
     var dataCopy = data; 
     var lengthCopy = length; 

     this.WorkMethod(busCopy, dataCopy, lengthCopy); 
    }); 
} 

Ist die (der obige Code) notwendig, um length und bus von Wert zu erhalten?

In diesem Fall wird CustomObject[] data (einige Klasse habe ich erstellt) als Referenz oder Wert übergeben?

+0

Variablen werden erfasst, keine Werte. Da diese Variablen für die Methode lokal sind, müssen Sie sie nicht kopieren, da nichts anderes sie modifizieren kann. – Lee

+0

Ein guter Hinweis auf diese Notiz ist [Eric Lippert - Schließen über die Schleife Variable als schädlich angesehen] (https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable - als schädlich angesehen/ –

Antwort

1

Was Sie übergeben, ist keine By-Value-Kopie.

Wenn Sie die Werte vor dem Ausführen der Aktion nicht ändern, müssen Sie sich keine Gedanken darüber machen, wie Sie die Werte übergeben. Aber nein, sie sind nicht wertlos. Auch wenn Sie die Aktion und Invoke es von einer anderen Methode zurückgeben. Die Werte werden in einer vom Compiler generierten Klasse beibehalten. Keine Notwendigkeit, sich darum zu kümmern

Wenn Sie erwarten, dass sich die Daten vor dem Ausführen der Aktion ändern, dann machen Sie es falsch. Die von Ihnen verwendete Methode (Kopieren eines Werttyps in eine lokale Variable) sollte außerhalb der Aktion und nicht innerhalb der Aktion erfolgen. Wie für den Referenztyp (Array), selbst wenn Sie ihn in eine lokale Variable kopieren, wird der kopierte Verweis reflektiert, so dass jede Änderung in der lokalen Kopievariablen widergespiegelt wird.

private void CreateAction() 
{ 
    int bus = 4; 
    CustomObject[] data = new object[16]; 
    int length = 1500; 

    var busCopy = bus; // a copy of bus 
    var dataCopy = data; // reference copy 
    var lengthCopy = length; // a copy of length 

    this.doWorkLater = new Action(() => 
    { 
     this.WorkMethod(busCopy, dataCopy, lengthCopy); 
    }); 

    bus = 10; // No effect on the action 
    length = 1700; // No effect on the action 

    this.doWorkLater(); 
} 

Das sieht sinnlos aus, aber Sie müssen manchmal eine lokale Variable in eine andere lokale Variable kopieren, bevor Sie sie an eine anonyme Methode übergeben. Überprüfen Sie diese Valid Example, die ein gemeldetes unerwartetes Verhalten behebt!

+0

Scheint lächerlich, die Objekte zu kopieren, die ich gerade erstellt habe ... oder? Ich denke, da stimmt etwas nicht. – Snoopy

+0

@StevieV Nein, es ist nicht lächerlich. Es gibt gültige Fälle, wo Sie das tun müssen. Wenn Sie es nach Wert an eine anonyme Methode übergeben möchten. Überprüfen Sie dieses gültige Beispiel: http://stackoverflow.com/a/38309084/3185569 – user3185569

+0

Es ist nicht so, als müsste ich innerhalb einer Schleife schließen. Was du tust macht keinen Unterschied. – Snoopy

1

Closures capture Werte beobachtbar durch Verweis * - beachten Sie, dass Code, den Sie haben Problem für die allgemeinen Fall nicht lösen, auch wenn ganzer Sinn CreateAction ist es funktionieren wird eine einzige Aktion erstellen.

private void CreateAction() 
{ 
    int bus = 4; 

    this.doWorkLater = new Action(() => 
    { 
     var busCopy = bus; 

     this.WorkMethod(busCopy); 
    }); 
    // if you change local `bus` before call to `doWorkLater` it will not work: 
    bus = 42; 
    doWorkLater(); // busCopy is 42. 
} 

* Es sammelt eigentlich alle Variablen in einem Compiler erstellt Klasse und verwendet, um es Referenzvariablen in einem Verfahren und einer Schließung zuzugreifen. Daher verhalten sich selbst Werttypen so, als ob sie als Referenz übergeben würden.

+0

Warum sagen Sie, dass es für den allgemeinen Fall nicht funktioniert? – Snoopy

+0

Lassen Sie uns sagen, ich lasse die Methode direkt nach dem Erstellen der Schließungen und dieser 'Bus' wird nie geändert. Bekomme ich einen Fehler, weil "bus" nicht mehr existiert? – Snoopy

+0

@StevieV Führen Sie den Code aus und finden Sie es heraus. Du hast es schon geschrieben. – Servy

0

Dies könnte Ihnen helfen, herauszufinden, was vor sich geht.

Wenn Sie mit diesem leicht vereinfachte Klasse beginnen:

public class Example 
{ 
    private void CreateAction() 
    { 
     int bus = 4; 
     object[] data = new object[16]; 
     int length = 1500; 

     Action doWorkLater =() => 
     { 
      var busCopy = bus; 
      var dataCopy = data; 
      var lengthCopy = length; 

      this.WorkMethod(busCopy, dataCopy, lengthCopy); 
     }; 

     doWorkLater.Invoke(); 
    } 

    public void WorkMethod(int bus, object[] data, int length) 
    { 
    } 
} 

... dann erzeugt der Compiler im Grunde diese:

public class Example 
{ 
    private void CreateAction() 
    { 
     Example.GeneratedClass closure = new Example.GeneratedClass(); 
     closure.parent = this; 
     closure.bus = 4; 
     closure.data = new object[16]; 
     closure.length = 1500; 

     // ISSUE: method pointer 
     IntPtr method = __methodptr(closure.CreateAction); 
     new Action((object)closure, method)(); 
    } 

    public void WorkMethod(int bus, object[] data, int length) 
    { 
    } 

    [CompilerGenerated] 
    private sealed class GeneratedClass 
    { 
     public int bus; 
     public object[] data; 
     public int length; 
     public Example parent; 

     internal void CreateAction() 
     { 
      this.parent.WorkMethod(this.bus, this.data, this.length); 
     } 
    } 
} 

Die lokalen in der Schließung aufhören gefangen Variablen sind lokale Variablen die Methode und werden öffentliche Felder in der Klasse generieren.

Jetzt gilt alles, was Sie über C# und Klassen wissen.

Verwandte Themen