6

Assuming my Awesome class has a property Bazinga, which is a String with the default value of "Default value", I could go:

    var enumerableOfAwesome = Enumerable
        .Range(1, 5)
        .Select(n => new Awesome());

    foreach (var a in enumerableOfAwesome)
        a.Bazinga = "New value";

As you may already be aware, enumerating the enumerableOfAwesome again will not make you happy, if you expect to find your "New value" strings safely tucked away in all the Bazinga. Instead, outputting them to the console would render:

Default value
Default value
Default value
Default value
Default value

(.NET Fiddle here)

This is all well and dandy from some deferred execution point-of-view, and that issue has been discussed already before, like here and here. My question however is why the enumerator implementations do not return immutable objects; If persistence over several enumerations is not guaranteed, then what use is there of the ability to set values, save making people confused?

Maybe this isn't a very good question, but I'm sure your answers will be enlightening.

Community
  • 1
  • 1
Oskar Lindberg
  • 2,255
  • 2
  • 16
  • 36
  • 1
    ToList() or ToArray() are your friends in this situation. And understanding what they do may help you to understand what's going on. – Joe Jun 02 '15 at 15:17
  • Because there's no magical immutability wrapper in C#, the enumerable is just returning an instance of a type that defines its own state and mutability. – Preston Guillot Jun 02 '15 at 15:21
  • 1
    A LINQ query is really just a set of instructions that define what should be executed on a certain collection and which return a new collection. If you execute it twice, you will perform the instructions on the collection twice -- that's it. If you don't store those results in a collection and use that collection, you will start from the same point twice. – Jeroen Vannevel Jun 02 '15 at 15:21

2 Answers2

9

My question however is why the enumerator implementations do not return immutable objects

How would you expect them to do that? They're sequences of whatever type is used - what would you expect to happen with something like:

var builders = new string[] { "foo", "bar" }.Select(x => new StringBuilder(x));

? Would you expect it (whatever "it" is in this case) to create a new type automatically with the same API as StringBuilder, but magically making it immutable? How would it detect mutations?

Basically, what you're asking for is infeasible - you can have a sequence where the element type is mutable, so while iterating through it you can mutate the object that the yielded reference refers to. Expecting anything else is unrealistic, IMO.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I'm not sure how to react to "infeasible". I realize that it would pose a challenge, but yes "magically making it immutable" would be fine by me. Should I read your answer as "it's a stupid notion" or as "there is no practical solution"? – Oskar Lindberg Jun 02 '15 at 15:32
  • 1
    @OskarLindberg, Its both a bad notion and impractical. If you want your `Awesome` to be immutable, you have to write it that way. For example you could create a class called `AwesomeReadOnly` that can be cast from the `Awesome` type that is an immutable object. How you implement it is up to you, its beyond what I believe a compiler should be doing behind the scenes to mangle your code. There are plenty of cases when you want this to be mutable too. – Ron Beyer Jun 02 '15 at 15:37
  • The solution for wanting to easily project into something immutable is also already covered by anonymous types. – Preston Guillot Jun 02 '15 at 15:38
  • @PrestonGuillot: Not really - if you have an anonymous type where one of the members is mutable, it's still only *shallow* immutability - change my example to `new { Builder = new StringBuilder(x) }` and you'd still be able to do `item.Builder.Append("xx")` – Jon Skeet Jun 02 '15 at 15:40
  • 1
    @OskarLindberg: While Entity Framework etc are "somewhat magic", LINQ to Objects is anything but - and that's the way I like it. The compiler behaves in a very predictable way, and the actual code implementing LINQ to Objects is pretty simple. I really wouldn't want something creating type facades, possibly cloning objects "just in case" etc in the background. If you want immutability, you go do that yourself. – Jon Skeet Jun 02 '15 at 15:44
  • I'm sure you realize I don't want unpredictable behavior in code any more than you do - magic was your suggestion. I never claimed to actually _want_ immutability; "Go do that yourself" is obviously fine - regardless of assuming mutability or immutability to be the default - but that's besides the point. In any case, I'll accept your answer as: "Because there's no practical way of achieving that (and besides, it might not be a brilliant idea)". – Oskar Lindberg Jun 02 '15 at 16:33
  • @OskarLindberg: It wasn't a suggestion - I was asking what you were expecting. I generally *don't* expect magic, and if I see magic I try to dig into it :) – Jon Skeet Jun 02 '15 at 16:44
0

Enumerators are not meant to provide immutability. They are a way to loop through collections like in looping through standard arrays with indexes. As you can access underlaying objects this means you can change them. And yeah immutability in C# is more of a new thing in terms of collections and this would be unexpected and impossible to implement in Enumerators.

fsacer
  • 1,382
  • 1
  • 15
  • 23
  • Just because you can loop them doesn't mean they are mutable. You can loop through immutable arrays/lists/enumerables too. – Ron Beyer Jun 02 '15 at 15:26