score:11

Accepted answer

First, there is no anonymous type involved.


This string[] arr = { "Agnes", "Allan", "Benny" }; is an array creation expression.

result is IEnumerable<string> and in both LINQ statements you are just creating a query.

This is what is going on:

array creation expression

string[] arr = { "Agnes", "Allan", "Benny" }; 

query arr and returns IEnumerable<string>

var result = arr.Where(a => a.StartsWith("A")).Select(a => a); 

assigns results a new query on arr returning IEnumerable<string>

result = arr.Where(a => a.EndsWith("n")).Select(a => a); 

As far as, for understanding immutability, think of String also see this article: Immutable types: understand their benefits and use them

score:3

You have an anonymous type when you do something like:

var anon = new { X = 5, Y = 6 };

There are some pretty simple rules: you can't express the type of an anonymous type (so often you use var)... there must be a new {... You must give a name to the properties and a value X = 5.

What you are doing is creating an array of string[] using an array initializer. You are even writing it:

string[] arr = ...

And you aren't modifying anything... result is another variable, referencing an IEnumerable<> (a new object you are creating) and then referencing another IEnumerable<> At the end of your code you have 6 objects (a little more, but we will ignore some invisible objects):

  • The array referenced by arr (and referenced by the two IEnumerable<>)
  • The second IEnumerable<>, referenced by result, that has a reference to arr
  • The first IEnumerable<>, not referenced by anyone (the GC will collect it before or later), that has a reference to arr
  • 3x string, all referenced by arr. Note that IEnumerable<> are "lazy", so they don't contain any reference to any string

The result variable is assigned twice, to two different IEnumerable<>. It is nearly always legal to reassign variables (exception are readonly fields). it is clearly legal to do:

string foo = "Foo";
foo = "Bar";

score:1

LINQ methods like Where() and Select() don't change the underlying array. It creates a new object. The created result is of type IEnumerable<string>, LINQ just filters the array so if you will iterate over it later, you will just get values that match Where and Select but your arr object will remain unchanged.

score:2

Another useful concept to understand is the difference between type, instance and variable.

Simplifying, type is like a blueprint, it describes what an instance of the type will look like:

class Car
{
   public int Doors {get; set;}
   public string EngineType { get; set;}
}

The code above describes type. You can make many instances of this type:

Car truck = new Car { Doors = 2, EngineType = "BigEckingEngine" };  
Car pickup = new Car { Doors = 5, Engine Type = "4 cylinder" };

etc... Note how variables truck and pickup house your instances. But variables are just that, they can house any instance of their respective type, so while it does not make much sense you can do this:

Car tmp = truck;
truck = pickup;
pickup = tmp;

The instances themselves has not changed. But the variables now hold different instances.

The instances of this example Car class above are mutable. So you can do this:

pickup.Doors = 99;

Should the type be immutable, you would not be able to do that, but you are still can do variable assigning as in the example with the tmp variable freely, regardless of type being mutable or not, because such assignment do not change instances.

As noted, your example does not contain an anonymous type, but even if it did, it does not involve any kind of mutation you are asking about.

score:1

It's worth expanding the other answers to show that a CONCRETE resolution of a LINQ query is not the same as a IEnumerable<T> and that neither have anything to do with anonymous type immutability.

If you created the following array of anonymous types:

var arr = new[] { new { Name = "Agnes"}, new { Name = "Allan" }, new { Name = "Benny" }};

arr.GetType().Dump();

var result = arr.Where(a => a.Name.StartsWith("A")).Select(a => a)
result = arr.Where(a => a.Name.EndsWith("n")).Select(a => a);

result.Dump();

in my case,

<>f__AnonymousType0`1[System.String][] 

and

"Allan"

are respectively outputted, because result type is actually

System.Linq.Enumerable+WhereSelectArrayIterator`2[
    <>f__AnonymousType0`1[System.String],
    <>f__AnonymousType0`1[System.String]]

In addition, if I try to resolve the IEnumerable and then re-update the result:

var result = arr.Where(a => a.Name.StartsWith("A")).Select(a => a).ToList();
result = arr.Where(a => a.Name.EndsWith("n")).Select(a => a).ToList();

I once again get an output of

"Allan"

However, in this case my result type has been reevaluated to

System.Collections.Generic.List`1[<>f__AnonymousType0`1[System.String]]

since ToList() is creating a new collection. I can technically add and remove to that collection at will since the collection itself is quite willing to be mutated.

Finally, that does not mean that the underlying anonymous type object is not immutable!

result.First ().Name = "fail";

Will fail, regardless of result being a list with the following error:

Property or indexer 'AnonymousType#1.Name' cannot be assigned to -- it is read only

precisely because it is immutable.


Related Articles