为什么IEnumerable变量的项目在指向IEnumerable时无法更新,但在指向已实现的集合时有效

Men*_*nol 2 c# collections ienumerable list

我已经阅读了IEnumerable中的更新对象<>未更新?更新IEnumerable中的项目属性但该属性没有保持设置?但我想了解当我尝试更新IEnumerable中的项目时它为什么不起作用.

我不明白的是:

当源集合指向Ienumerable时,我无法使用.ElementAt()方法更新Ienumerable的项目.

但是,当源Ienumerable指向列表时,相同的代码可以正常工作.

1有人可以帮助解释幕后发生的事情吗?

2为什么C#编译器没有(或不能)错误?

3当我们必须在需要更新时将Ienumerables转换为Lists时,使用Ienumerable来定义类型也不会失效吗?

我相信我在这里遗漏了一些东西,如果有人可以解释幕后发生的事情,我会很高兴.

这是我的代码:

1 - 收藏不会更新:

class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var numbers = new int[] {1,2,3};

        var people = numbers.Select(n => new Person { Name = "Person " + n.ToString() });


        for (int i = 0; i < people.Count(); i++)
        {
            people.ElementAt(i).Name = "New Name";
        }

        people.ToList().ForEach(i => Console.WriteLine(i.Name));

        // OUTPUT: Person1, Person2, Person3
    }

}
Run Code Online (Sandbox Code Playgroud)

2 - 当我将.ToList()添加到集合创建行时,集合会更新(注意Main方法中的第3行)

class Person
{
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        var numbers = new int[] {1,2,3};

        // Note the .ToList() at the end of this line.
        var people = numbers.Select(n => new Person { Name = "Person " + n.ToString() }).ToList();


        for (int i = 0; i < people.Count(); i++)
        {
            people.ElementAt(i).Name = "New Name";
        }

        people.ToList().ForEach(i => Console.WriteLine(i.Name));


        Console.ReadLine();
        // OUTPUT: New Name, New Name, New Name
    }

}
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 8

与往常一样,您需要记住LINQ操作返回查询,而不是执行这些查询的结果.

当您调用select时返回的是一个查询,该查询知道每次要求项目时如何将源集合中的每个项目投影到新项目中.每次迭代时,IEnumerable您都在执行查询新的时间,每次都创建一个新的结果集(由所有新项组成).你可以,实际上更新每次通话时间的项目ElementAt,这就是为什么编译器不会阻止你.除了设置一个值然后将其丢弃在地板上之外,你只是对该项目一无所知,永远不会再次使用.执行时:

people.ElementAt(i).Name = "New Name";
Run Code Online (Sandbox Code Playgroud)

你正在迭代numbers,Person为每个数字创建一个新的,直到你得到第i个值,然后它返回ElementAt,此时设置名称,然后你的代码无法再访问该值,因为你没有 ' Person在任何地方都可以保存.当你打电话给ElementAt(0)你创建一个人0,设置名称,然后什么都不做.当你打电话给ElementAt(1)你创建两个人时,返回第二个,设置它的名字,然后忘记它.然后你打电话ElementAt(2),再创造3个人,返回第3个,设置它的名字,然后忘掉它.然后你打电话ToList,创建一个包含所有3个人的列表,并打印出这3个全新人的价值观.

当您将查询具体化为集合时,您可以多次访问该集合中的项目,因为您具有多次访问的查询结果,并跟踪那些返回的结果,而不是查询本身就是你一遍又一遍地执行.