2016-07-28 10 views
0

我正在創建一個匿名方法並將其傳遞給稍後調用的操作。我想將一些數字數據(int)傳遞給我的匿名方法。爲了通過價值傳遞數據,我是否需要創建副本?或者,數據是否會按價值傳遞?傳遞給動作的匿名方法是否通過值或引用獲取數據?

以下是我認爲實現會是什麼樣子,如果我不得不創建副本:

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

是必要的這個(上面的代碼),以獲得lengthbus的價值?

在這種情況下,將CustomObject[] data某些類創建的)通過引用或值傳遞?

+0

變量被捕獲,而不是值。由於這些變量對於方法來說是本地的,所以您不需要複製它們,因爲沒有其他可以修改它們的方法。 – Lee

+0

在這個筆記上的一個很好的解讀是[Eric Lippert - 關閉被認爲有害的循環變量](https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable -considered有害/)。 –

回答

1

你傳遞的不是按值副本。

如果您在執行操作之前不打算修改這些值,那麼您無需擔心如何傳遞值。但是,沒有他們沒有被價值傳遞。即使你從另一種方法返回行動並Invoke它。這些值將保存在由編譯器生成的類中。無需擔心

如果您希望在執行操作之前更改數據,那麼您做錯了。您正在使用的方法(將值類型複製到局部變量)應該在動作之外進行,而不是在動作之內。至於引用類型(Array),即使將其複製到局部變量,也會複製其引用,以便反映副本局部變量中的任何更改。

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

這看起來毫無意義的,但你有時可能需要將它傳遞給一個匿名方法之前,一個局部變量複製到另一個局部變量。檢查此Valid Example修復報告的意外行爲!

+0

似乎荒謬地複製我剛剛創建的對象......不是嗎?我認爲這有些問題。 – Snoopy

+0

@StevieV不,它不是可笑的。有些情況下你需要這樣做。如果你想通過價值傳遞給匿名方法。檢查這個有效的例子:http://stackoverflow.com/a/38309084/3185569 – user3185569

+0

這不是像我需要關閉循環內。所以你在做什麼並沒有什麼不同。 – Snoopy

1

閉包捕獲值明顯地通過引用* - 請注意,您的代碼並不能解決一般情況下的問題,同樣如果整個點爲CreateAction創建一個單一的動作,它將工作。

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. 
} 

*它實際上收集所有的變量在編譯器創建的類,並使用引用來訪問的方法和閉包變量。因此,即使值類型看起來像通過引用傳遞一樣。

+0

爲什麼你說它不適用於一般情況? – Snoopy

+0

假設我在創建閉包之後立即離開該方法,並且'bus'永遠不會改變。我會因爲'bus'不存在而出現某種錯誤嗎? – Snoopy

+0

@StevieV運行代碼並找出答案。你已經寫好了。 – Servy

0

這可能會幫助你弄清楚發生了什麼。

如果在開始使用此稍作簡化類:

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) 
    { 
    } 
} 

...那麼編譯器基本上產生這樣的:

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

被困在封閉停止局部變量是局部變量的方法併成爲生成類中的公共字段。

現在,您瞭解的有關C#和類的所有內容均適用。

相關問題