LINQ 表达式中的值是否通过引用传递?

发布于 2024-09-24 18:35:26 字数 996 浏览 3 评论 0原文

我正在阅读有关 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

怪我太投入 2024-10-01 18:35:26

这与惰性执行有关。每次迭代查询时,都会再次查看数字。事实上,如果您在执行查询时更改 numbers 的后期元素的值,,您也会看到该变化。这一切都改变了数组的内容

请注意,查询会记住创建查询时的 numbers 值 - 但该值是一个引用,而不是数组的内容。因此,如果您像这样更改 numbers 本身的值:

numbers = new int[] { 10, 9, 8, 7 };

那么该更改将不会反映在查询中。

让事情变得复杂的是,如果您在查询的其他部分中使用变量,如下所示:

int x = 3;

var query = from n in numbers
            where n == x
            select Square(n);

那么将捕获 变量 x 而不是它的值...因此更改 x 将更改评估查询的结果。这是因为查询表达式实际上被转换为:

var query = numbers.Where(n => n == x)
                   .Select(n => Square(n));

请注意,这里,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 of numbers 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 of numbers itself like this:

numbers = new int[] { 10, 9, 8, 7 };

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:

int x = 3;

var query = from n in numbers
            where n == x
            select Square(n);

then the variable x is captured rather than its value... so changing x will change the results of evaluating the query. That's because the query expression is really translated to:

var query = numbers.Where(n => n == x)
                   .Select(n => Square(n));

Note that here, x is used within a lambda expression, but numbers isn't - that's why they behave slightly differently.

杯别 2024-10-01 18:35:26

数字的引用通过传递。但是,查询是延迟计算的,并且底层数组是可变的。

那么这意味着什么呢?

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!

一般来说,如果您更改变量而不是其底层对象,它很快就会变得混乱,因此最好避免这样的更改。

例如:

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

要理解这一点,你需要了解编译过程的细节。查询表达式被定义为相当于大量使用闭包的扩展方法语法 (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?

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!

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:

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

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.

秋意浓 2024-10-01 18:35:26

查询完全按照原样存储 - 不是结果集,只是查询。

当您请求查询结果时,它会使用执行查询时的当前值(而不是创建查询时的值)来评估查询。如果您对同一查询进行两次评估,如果基础数据已更改,您可能会得到不同的结果,如问题中提供的示例所示。

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.

各空 2024-10-01 18:35:26

这是因为对 numbers 的引用是一个闭包并与枚举的延迟执行相结合,给出了这个结果。

This is because the reference to numbers is a closure and combined with lazy execution of the enumeration it gives this result.

耳钉梦 2024-10-01 18:35:26

是的,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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文