2013-07-02 65 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)是值类型的副本,而不是引用。