kof*_*cii 3 linq pass-by-reference
我正在阅读关于LINQ的manning书,有一个例子:
static class QueryReuse
{
static double Square(double n)
{
Console.WriteLine("Computing Square("+n+")...");
return Math.Pow(n, 2);
}
public static void Main()
{
int[] numbers = {1, 2, 3};
var query =
from n in numbers
select Square(n);
foreach (var n in query)
Console.WriteLine(n);
for (int i = 0; i < numbers.Length; i++)
numbers[i] = numbers[i]+10;
Console.WriteLine("- Collection updated -");
foreach (var n in query)
Console.WriteLine(n);
}
}
Run Code Online (Sandbox Code Playgroud)
具有以下输出:
Computing Square(1)...
1
Computing Square(2)...
4
Computing Square(3)...
9
- Collection updated -
Computing Square(11)...
121
Computing Square(12)...
144
Computing Square(13)...
169
Run Code Online (Sandbox Code Playgroud)
这是否意味着'数字'是通过引用传递的?这种行为是否必须对延迟执行和收益做些什么?或者我在这里走错了路?
这与懒惰的执行有关.每次遍历查询时,都会numbers再次查看.实际上,如果在执行查询numbers 时更改后期元素的值,您也会看到更改.这都是改变数组的内容.
请注意,查询会记住numbers查询创建时的值 - 但该值是引用,而不是数组的内容.所以,如果你numbers像这样改变自己的价值:
numbers = new int[] { 10, 9, 8, 7 };
Run Code Online (Sandbox Code Playgroud)
然后该更改将不会反映在查询中.
只是为了使事情复杂化,如果在查询的其他部分中使用变量,如下所示:
int x = 3;
var query = from n in numbers
where n == x
select Square(n);
Run Code Online (Sandbox Code Playgroud)
然后捕获变量 x而不是其值...因此更改x将更改评估查询的结果.那是因为查询表达式真的被翻译成:
var query = numbers.Where(n => n == x)
.Select(n => Square(n));
Run Code Online (Sandbox Code Playgroud)
请注意,这里x使用的是lambda表达式,但numbers不是 - 这就是为什么它们的行为略有不同.
参考到numbers由传递值.但是,查询是懒惰地评估的,并且底层数组是可变的.
那是什么意思呢?
var arr = new[]{1,2,3,};
var q = arr.Select(i=>i*2);
Console.WriteLine(string.Join(", ",q.ToArray())); //prints 2, 4, 6
arr[0]=-1;
Console.WriteLine(string.Join(", ",q.ToArray())); //prints -2, 4, 6
// q refers to the original array, but that array has changed.
arr = new[]{2,3,4};
Console.WriteLine(string.Join(", ",q.ToArray())); //prints -2, 4, 6
//since q still refers to the original array, not the variable arr!
Run Code Online (Sandbox Code Playgroud)
通常,如果您更改变量而不是基础对象,它会很快变得混乱,因此最好避免这样的更改.
例如:
var arr = new[]{1,2,};
var arr2 = new[]{1,2,};
var q = from a in arr
from b in arr2
select a*b;
// q is 1,2,2,4
arr = new[]{0,1}; //irrelevant, arr's reference was passed by value
// q is still 1,2,2,4
arr2 = new[]{0,1}; //unfortunately, relevant
// q is now 0, 1, 0, 2
Run Code Online (Sandbox Code Playgroud)
要理解这一点,您需要了解编译过程的详细信息.查询表达式被定义为等效于扩展方法syntax(arr.Select...),它大量使用闭包.因此,实际上只有第一个可枚举或可查询的引用按值传递,其余的都在闭包中捕获,这意味着它们的引用通过引用有效传递.困惑了吗? 避免更改此类变量以保持代码的可维护性和可读性.
| 归档时间: |
|
| 查看次数: |
2245 次 |
| 最近记录: |