Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does setting a property on an enumerated object not work?

Tags:

c#

ienumerable

I know a lot about C# but this one is stumping me and Google isn't helping.

I have an IEnumerable range of objects. I want to set a property on the first one. I do so, but when I enumerate over the range of objects after the modification, I don't see my change.

Here's a good example of the problem:

    public static void GenericCollectionModifier()
    {
        // 1, 2, 3, 4... 10
        var range = Enumerable.Range(1, 10);

        // Convert range into SubItem classes
        var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i});

        Write(items);  // Expect to output 1,2,3,4,5,6,7,8,9,10

        // Make a change
        items.First().MagicNumber = 42;

        Write(items);  // Expect to output 42,2,3,4,5,6,7,8,9,10
        // Actual output: 1,2,3,4,5,6,7,8,9,10
    }

    public static void Write(IEnumerable<SubItem> items)
    {
        Console.WriteLine(string.Join(", ", items.Select(item => item.MagicNumber.ToString()).ToArray()));
    }

    public class SubItem
    {
       public string Name;
       public int MagicNumber;
    }

What aspect of C# stops my "MagicNumber = 42" change from being output? Is there a way I can get my change to "stick" without doing some funky converting to List<> or array?

Thanks! -Mike

like image 949
Mike Avatar asked Oct 22 '25 03:10

Mike


1 Answers

When you call First() it enumerates over the result of this bit of code:

Select(i => new SubItem() {Name = "foo", MagicNumber = i});

Note that the Select is a lazy enumerator, meaning that it only does the select when you ask for an item from it (and does it every time you ask it). The results are not stored anywhere, so when you call items.First() you get a new SubItem instance. When you then pass items to Write, it gets a whole bunch of new SubItem instances - not the one you got before.

If you want to store the result of your select and modify it, you need to do something like:

var items = range.Select(i => new SubItem() {Name = "foo", MagicNumber = i}).ToList();
like image 158
Simon Steele Avatar answered Oct 23 '25 16:10

Simon Steele