iEnumerable是可读的..但是它不是吗?

发布于 2025-02-08 00:20:50 字数 1610 浏览 0 评论 0原文

这个问题的许多主题。想知道为什么有时收藏会被阅读,有时没有。我在.NET Core 3.1项目中遇到了一个问题,其中目的是修改foreach循环中的集合。这个问题是在迭代之后,但是根本没有修改收集……根据我的理解,这是有道理的。

public async Task DoFoo(IEnumerable<SomeClass> data, CancellationToken cancellationToken)
{
        foreach (var item in data)
        {
            item.Id = item.SomeOtherValue;
            //note: checked and Id is {get;set;}
        }

        await SaveData(data, cancellationToken);
}

结果是ID仍然为无效。铸造以列出然后修改解决问题。

但是在.NET小提琴上进行测试( https://dotnetfiddle.net/bqvf40 被更改。这确实不是我所期望的。任何人都可以解释为什么这会发生变化。

using System;
using System.Diagnostics;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var iePerson = new[] { new Person(){Id = 2, Name="SomeName", Other=5} }; //note other = 5
        IEnumerable<Person> pien = iePerson; //writing this way to ensure we are creating IEnumable
        DoFoo(pien); 
    }
    
    private static void DoFoo(IEnumerable<Person> entities)
    {
        foreach(Person p in entities)
        {
            Console.WriteLine(p.Id);
            p.Id = p.Other;
        }
                
        foreach(Person p in entities)
            Console.WriteLine(p.Id);
                
        //result is 
        //2
        //5 <--- was expecting to see 2... 

    }
    
    public class Person
    {
        public string Name {get;set;}
        public int Id {get;set;}
        public int Other {get;set;}
    }
}

Many topics on this question. Wondering why sometimes the collection is readonly, and sometimes not. I came across an issue in a .NET Core 3.1 project where the intention was to modify a collection within a foreach loop. The issue was after the iteration, however the collection was not modified at all... which based on my understanding makes sense.

public async Task DoFoo(IEnumerable<SomeClass> data, CancellationToken cancellationToken)
{
        foreach (var item in data)
        {
            item.Id = item.SomeOtherValue;
            //note: checked and Id is {get;set;}
        }

        await SaveData(data, cancellationToken);
}

The result was that id was still null. casting to list and then modifying fixed the issue.

Testing however on .net fiddle (https://dotnetfiddle.net/bQvf40) shows that the collection does in fact get altered. Which is really not what I expected. Can anyone explain why this is getting changed.

using System;
using System.Diagnostics;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var iePerson = new[] { new Person(){Id = 2, Name="SomeName", Other=5} }; //note other = 5
        IEnumerable<Person> pien = iePerson; //writing this way to ensure we are creating IEnumable
        DoFoo(pien); 
    }
    
    private static void DoFoo(IEnumerable<Person> entities)
    {
        foreach(Person p in entities)
        {
            Console.WriteLine(p.Id);
            p.Id = p.Other;
        }
                
        foreach(Person p in entities)
            Console.WriteLine(p.Id);
                
        //result is 
        //2
        //5 <--- was expecting to see 2... 

    }
    
    public class Person
    {
        public string Name {get;set;}
        public int Id {get;set;}
        public int Other {get;set;}
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(1

世界等同你 2025-02-15 00:20:50

看来您对list是什么以及enumerable是什么有误解。前者是实现的内存数据结构,这意味着存储器中存在的项目集合。您可以通过索引访问该集合中的项目,添加新项目或删除项目。

后者只是一个概括,只有您才能迭代该集合。但是,它不能保证该数据的来源。它可能是一个内存集合,例如列表,但是它也可能是数据库或某些流。在您的情况下至关重要。 每当您要求iEnumerable为您提供数据时,它都会在基础数据存储上执行查询 - 无论如何。在您的小提琴中,基础数据存储是一个数组 - 已经将其物质化为内存。但是,在您的应用中,有可能是一些流或数据库。因此,当您迭代枚举时,您再次执行查询。这也省略了您在两者之间进行的所有修改,因为您从未将这些更改传输到数据存储。

在您的集合上调用Tolist(顺便说一句,与铸件不同)将有助于查看应用程序中的更改,因为您将数据实现为内存并在此处修改。但是,它无助于您将这些更改保存回数据库。

另一方面,铸造不会改变基础数据存储。因此,如果它已经是一个数组,则将其下调为iEnumerable不会更改任何内容。当然,迭代两次枚举也将两次执行查询 - 但是由于该存储是一个数组,因此不会受伤。但是,在数据库上,它会受到很大的伤害。

为了使长篇小说简短:在您的真实情况下,您有一个数据库。更改客户端应用程序中的任何元素都不会自动反映在数据库中,无论它是列表,数组还是ienumerable。您可以看到客户端的更改,但是一旦再次查询项目(如果在ienumerable中完成,请参见上文),则会丢失更改。只有当您明确保存时,更改才会同步到您的数据库。

另一方面,您没有数据库,而是物质化(=内存) - 集合,因此根本不需要同步任何内容。您可以修改集合中的每个元素(再次,无论是数组,列表还是ienumerable),并查看客户端中的更改。

It seems you have a misunderstanding on what a List is and what an Enumerable is. The former is a materialized in-memory data-structure, that means a collection of items that exist in memory. You can access the items in that collection by index, add new items or remove items.

The latter is just a generalization which states only that you can iterate that collection. However it makes no guarantees about where that data comes from. It might be an in-memory collection such as a list, however it might also be a database or some stream. Which is crucial in your case. Every time you ask your IEnumerable to give you the data, it performs a query on the underlying data-store - whatever that might be. In your fiddle that underlying data-storage is an array - something that has already been materialized into memory. In your app however chances are it's some stream or a database. So when you iterate the enumerable, you execute the query again. This also omits every modification you did in between, because you never transmitted those changes back to the data-storage.

Calling ToList on your collection (which by the way is not the same as casting) will help to see the changes within your app, as you materialize the data into memory and modify it there. However it won't help you in saving those changes back into the database.

Casting on the other hand won't change the underlying data-storage. So if it's already an array, down-casting it to an IEnumerable won't change anything. Of course iterating that enumerable twice will also perform the query twice - but as that storage is an array, that won't hurt. On a database however, it will hurt much.

To keep long stories short: in your real-world scenarios you have a database. Changing any of its elements in your client-app won't be automatically reflected in the database regardless if it's a list, an array or just an IEnumerable. You can see the changes in your client, but as soon as you query the items again (which is done in case of IEnumerable, see above), the changes are lost. Only when you explicitely save it the changes are synced back to your database.

In your fiddle on the other hand you don't have a database, but a materialized (=in-memory)-collection, so there's no need to sync anything back at all. You can modify every element in your collection (again regardless if it's an array, a list or just an IEnumerable) and see the changes in your client.

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