Home > Blockchain >  IENumerable is readonly.. but it isn't?
IENumerable is readonly.. but it isn't?

Time:06-17

many topics on this question. Wondering why sometimes the collection is readonly, and sometimes not. I came across an issue in a netCore 3.1 project where the intention was to modify a collection within a foreach loop. The issue was after the itteration, 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;}
    }
}

CodePudding user response:

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. 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.

  • Related