LINQ 表达式中的值是否通过引用传递?
我正在阅读有关 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);
}
}
输出如下:
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
这是否意味着“数字”是通过引用传递的?这种行为是否与延迟执行和收益有关?或者我在这里走错了路?
I'm reading manning book about LINQ, and there is an example:
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);
}
}
with the following output:
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
Does this means, that 'numbers' is passed by reference? Does this behavior have to do something with lazy execution and yield? Or I'm on a wrong track here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这与惰性执行有关。每次迭代查询时,都会再次查看
数字
。事实上,如果您在执行查询时更改numbers
的后期元素的值,,您也会看到该变化。这一切都改变了数组的内容。请注意,查询会记住创建查询时的
numbers
值 - 但该值是一个引用,而不是数组的内容。因此,如果您像这样更改numbers
本身的值:那么该更改将不会反映在查询中。
让事情变得复杂的是,如果您在查询的其他部分中使用变量,如下所示:
那么将捕获 变量
x
而不是它的值...因此更改x
将更改评估查询的结果。这是因为查询表达式实际上被转换为:请注意,这里,
x
在 lambda 表达式中使用,但numbers
不是 - 这就是为什么它们的行为略有不同。It's to do with lazy execution. Every time you iterate through the query, that will be looking at
numbers
again. Indeed, if you change the value of a late element ofnumbers
while you're executing the query, you'll see that change too. This is all changing the contents of the array.Note that the query remembers the value of
numbers
at the time of the query creation - but that value is a reference, not the contents of the array. So if you change the value ofnumbers
itself like this:then that change won't be reflected in the query.
Just to complicate things, if you use variables within other parts of the query, like this:
then the variable
x
is captured rather than its value... so changingx
will change the results of evaluating the query. That's because the query expression is really translated to:Note that here,
x
is used within a lambda expression, butnumbers
isn't - that's why they behave slightly differently.对
数字
的引用通过值传递。但是,查询是延迟计算的,并且底层数组是可变的。那么这意味着什么呢?
一般来说,如果您更改变量而不是其底层对象,它很快就会变得混乱,因此最好避免这样的更改。
例如:
要理解这一点,你需要了解编译过程的细节。查询表达式被定义为相当于大量使用闭包的扩展方法语法 (
arr.Select...
)。因此,实际上只有第一个可枚举或可查询的引用按值传递,其余的都在闭包中捕获,这意味着它们的引用实际上是按引用传递的。还困惑吗? 避免更改这样的变量,以保持代码的可维护性和可读性。The reference to
numbers
is passed by value. However, the query is evaluated lazily, and the underlying array is mutable.So what does that mean?
Generally, it can get confusing pretty quickly if you change variables rather than their underlying objects, so it's better to avoid changes like this.
For example:
To understand this, you need to understand the details of the compilation process. Query expressions are defined as equivalent to an extension method syntax (
arr.Select...
) which uses closures heavily. As a result, effectively only the first enumerable or queryable has its reference passed by value, the rest are captured in closures, and that means their references are effectively passed by reference. Confused yet? Avoid changing variables like this to keep your code maintainable and readable.查询完全按照原样存储 - 不是结果集,只是查询。
当您请求查询结果时,它会使用执行查询时的当前值(而不是创建查询时的值)来评估查询。如果您对同一查询进行两次评估,如果基础数据已更改,您可能会得到不同的结果,如问题中提供的示例所示。
The query is stored as exactly that - not a result set, just a query.
When you request the results from the query it evaluates the query using the current values at the time the query is executed, not the values from when the query was created. If you evaluate the same query twice you can get different results if the underlying data has changed, as the example you have provided in the question demonstrates.
这是因为对
numbers
的引用是一个闭包并与枚举的延迟执行相结合,给出了这个结果。This is because the reference to
numbers
is a closure and combined with lazy execution of the enumeration it gives this result.是的,
numbers
变量是通过引用传递的,不是因为您使用 LINQ,而是因为数组是引用类型。输出变化的事实是由于 LINQ 的延迟/延迟计算造成的。
Yes, the
numbers
variable is passed by reference, not because you use LINQ, but because arrays are reference types.The fact that the output changes is due to the deferred/lazy evaluation of LINQ.