Foreach在什么情况下使用ref,在什么情况下使用副本?

using System;
using System.Linq;

class A {
    public int v;
}

class Program
{
    static void Main() {
        var ints = new int[] { 0, 1, 2 };
        
        var array = ints.Select(i=>new A {v = i}).ToArray();
        foreach(var a in array) {
            a.v = 999;
        }

        var enumerable = ints.Select(i=>new A {v = i});
        foreach(var a in enumerable) {
            a.v = 999;
        }

        Console.WriteLine($"array.First = {array.First().v}");
        Console.WriteLine($"enumerable.First = {enumerable.First().v}");
    }
}

Jdoodle.com/ia/jce

输出:

array.First = 999
enumerable.First = 0

似乎在foreach(var a in enumerable) {a中,a是复制而不是引用,而在foreach(var a in array) {a中,a是引用.

有没有人能解释一下这个?

推荐答案

这并不是真正意义上的foreach.它更多地与您如何构造arrayenumerable有关.foreach在这两种情况下执行相同的操作(获取枚举数并调用MoveNextCurrent来迭代可枚举数).正是enumerablearray之间的差异导致了yields 的差异.

arrayA[],所以如果你改变它的元素的v,然后得到这些元素的第一个,你显然会看到变化.A是引用类型,因此foreach中的a是对数组中元素的引用.

然而,enumerableSelect产生的东西.如果你只拨打Select,什么实质性的事情都做不了.它只是创建了一个IEnumerable<A>,即when enumerated over,创建了一堆A个对象.这里重要的一点是,每当枚举enumerable时,都会运行Select lambda.如果你不列举它,什么都不会发生.这就是所谓的deferred execution.

因此,第二个foreach枚举enumerable,创建一组A个对象,然后更改这A个对象中的v个.这里有一个重要的区别--A个对象不会存储在任何地方,这与数组不同.您在每次迭代后"丢弃"了A对象.

当您在结束时调用enumerable.First()时,您将再次开始枚举enumerable--这次只需要一次,因为您只需要第一个元素.enumerable是做什么的?它通过运行Select中的代码创建一个新的A对象.

Csharp相关问答推荐

System.Data.SQLite:判断SQLite数据库是否为空(任何表中至少有一行)

EF Core:看不到任何查询日志(log)?

FromServices不使用WebAppliationFactory程序>

Unity如何在PlayerPrefs中保存数据?

使用客户端密钥为Fabric Rest API生成令牌

Rider将.NET安装在哪里

在具有不同属性名称的两个类之间创建关系

C#带主体的主构造函数?

如何通过寻找向量长度来优化两个循环?

Blazor Web App WASM的两个独立项目令人困惑

在C#中反序列化/序列化具有混合元素顺序的XML时出现问题

C#中Java算法的类似功能

为什么方法的值在SELECT方法中不会更改?

对于PowerShell中的ConvertTo-SecureString方法,Microsoft如何将初始化向量添加到AES加密中的安全字符串?

C#按名称从类获取属性值类型<;t>;,我需要反射吗?

同一组件的多个实例触发相同的事件处理程序

C# Winforms:从对象树到TreeView的递归转换重复条目

多个参数的最小API删除

如何在单击按钮后多次异步更新标签

项目参考和方法签名问题