2013-07-02 106 views
2

對於你們中的一些人來說,這可能看起來微不足道,但我對以下兩個例子感到困惑。LINQ通過局部變量

int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
int i = 0; 

var simpleQuery = 
    from num in numbers 
    select ++i; 

foreach (var item in simpleQuery) 
{ 
    Console.WriteLine("v = {0}, i = {1}", item, i); // now i is incremented   
} 

輸出:

v = 1, i = 1 
v = 2, i = 2 
v = 3, i = 3 
v = 4, i = 4 
v = 5, i = 5 
v = 6, i = 6 
v = 7, i = 7 
v = 8, i = 8 
v = 9, i = 9 
v = 10, i = 10 

它更新我的價值,一切都很好了這一點。但是當我試圖更新數組的元素時,它不起作用。

int[] numbers = new int[] { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
var simpleQuery = 
    from num in numbers 
    select ++num; 

int i = 0; 
foreach (var item in simpleQuery) 
{ 
    Console.WriteLine("v = {0}, num = {1}", item, numbers[i++]); // now num is NOT incremented??? 
} 

輸出:

v = 6, num = 5 
v = 5, num = 4 
v = 2, num = 1 
v = 4, num = 3 
v = 10, num = 9 
v = 9, num = 8 
v = 7, num = 6 
v = 8, num = 7 
v = 3, num = 2 
v = 1, num = 0 

什麼可能是這背後的原因是什麼?

編輯: 我認爲第二個例子將輸出:

v = 6, num = 6 
v = 5, num = 5 
v = 2, num = 2 
v = 4, num = 4 
v = 10, num = 10 
v = 9, num = 9 
v = 7, num = 7 
v = 8, num = 8 
v = 3, num = 3 
v = 1, num = 1 
+0

您的代碼正常工作。您想做什麼? 'select ++ i;'不涉及你的數組。你只是增加我爲每個數字數組項目 –

+0

令人困惑的部分是,我的值因「select ++ i」而改變,但num的值不是 – sotn

回答

3

您的查詢實際上看起來像

numbers.Select(num => ++num) 

這實際上是調用擴展方法

Enumerable.Select(numbers, new Func<int, int>(num => ++num)) 

這種方法對陣列的每個項目執行選擇。選擇器是一個匿名函數(即該函數的名稱將由編譯器生成)。並且每個項目都傳遞給該函數。這裏是爲什麼數組中的項保持不變 - integer是一個值類型。值類型按值傳遞(即創建項目的副本,而不是傳遞對項目的引用)。因此,內部選擇器副本被修改並返回。這不會影響數組中的原始項目。

在第一種情況下,你已經在委託拍攝的參考變量i,這就是爲什麼i改變:

Enumerable.Select(numbers, new Func<int, int>(num => ++i)) 

陣列項目仍然會在這裏傳遞的方法參數,但i在方法體捕獲。實際上,這些變量(它是方法體的一部分)被編譯器取代 - 在第一種情況下,變量i被移動到類的字段中,並且對該變量的所有調用都被對類字段的調用替換。即在第一種情況下編譯的代碼看起來像:

Foo foo = new Foo(); 
foreach(int num in numbers) 
    Console.WriteLine(foo.Bar(num)); 

Foo是一個產生的嵌套類,並Bar是選擇代表,這是一種方法,在該類生成。

private class Foo 
{ 
    private int _i; // variable is captured by delegate 

    public int Bar(int x) 
    { 
     _i = _i + 1; // thats why it has new value on next call 
     return _i; 
    } 
} 
+1

謝謝先生,這使事情變得清晰 – sotn

+1

@BerkArslan我添加了示例,它更詳細地解釋了爲什麼您第一次編碼的作品。我認爲這將是有益的調查:) –

0
var simpleQuery = 
from num in numbers 
select ++i; 

指:

for(int i=0; i<numbers.Length; i++) 
{ 
    simpleQuery.Add(++i); 
} 

而且

var simpleQuery = 
from num in numbers 
select ++num; 

種手段:

for(int i=0; i<numbers.Length; i++) 
{ 
    simpleQuery.Add(numbers[i] + 1); 
} 
+0

所以你認爲++我會改變我的值而++ num不會更改相應數組項目的值 – sotn

+0

您想要做什麼?或者你只是想了解你的代碼工作 –

+0

在你的第二個例子中,你獲取每個項目的+1。 –

1

你的第一個查詢到i基準(捕獲爲閉合的一部分)操作,而第二個查詢在每個數組元素的副本,而不是參考操作。傳遞給LINQ查詢的表達式映射到lambda表達式,並且輸入到lambda表達式的變量(例如在您的示例中爲num)是值類型的副本,而不是引用。