Why can an anonymous type be changed if it should be immutable?

I thought I had a good understanding of anonymous type, but this little piece of code confused me a bit:

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

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

// Why can I do the below, if arr is immutable?
result = arr.Where(a => a.EndsWith("n")).Select(a => a);

      

I don't understand why the second value is assigned to me result

. I mean, the idea of ​​anonymous types is not immutable, that they cannot be changed after they have their original value?

+3


source to share


5 answers


First, there is no anonymous type .


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

is an array creation expression .

result

- IEnumerable<string>

, and in both LINQ operators, you are just creating a query.

This is what happens:

array creation expression



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

      

arr request and returns IEnumerable<string>

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

      

assigns the results to a new arr query, returning IEnumerable<string>

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

      

As for understanding immutability, consider String

also see this article: Immutable Types: Understand Their Benefits and Use Them

+11


source


You have an anonymous type when you do something like:

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

      

There are some simple rules: you cannot express the type of an anonymous type (as you often do var

) ... there should be new {

... you must provide a name for the property and a value X = 5

.

What you are doing is creating an array string[]

using an array initializer. You even write this:

string[] arr = ...

      



And you don't change anything ... result

is another variable referencing IEnumerable<>

(the new object you are creating) and then referencing another IEnumerable<>

. At the end of your code, you have 6 objects (a bit more, but we'll ignore some invisible objects):

  • Array referenced by arr

    (and referenced by two IEnumerable<>

    )
  • Second IEnumerable<>

    referenced result

    which has a link toarr

  • The first one IEnumerable<>

    , which is not referenced by anyone (GC collects it before or later), which has a link toarr

  • 3x string

    , all refer to arr

    . Note that they IEnumerable<>

    are "lazy", so they do not contain links to anystring

The variable result

is assigned twice, up to two different ones IEnumerable<>

. Almost always the right to reassign variables (except for fields readonly

). this is clearly legal:

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

      

+3


source


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

Simplification, the type is like a blueprint, it describes what an instance of the type would look like:

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

      

The above code describes the 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 ... Notice how the variable trucks and pickup will fit your instances. But variables are just like that, they can place any instance of their type, so while it doesn't really matter, you can do this:

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

      

The instances themselves have not changed. But now the variables have different instances.

Instances of this example Car

class above are mutable. So, you can do this:

pickup.Doors = 99;

      

If the type is immutable, you cannot do this, but you can still assign variables, as in the variable example tmp

, no matter what type is changed or not, since such assignment does not change instances.

As noted, your example does not contain an anonymous type, but even if it did, it does not include any mutation you are asking for.

+2


source


LINQ methods like Where()

and Select()

do not modify the underlying array. It creates a new object. The created one result

is of type IEnumerable<string>

, LINQ just filters the array, so if you iterate over it later, you just get the values ​​that match Where

and Select

, but your object arr

remains unchanged.

+1


source


It is worth expanding on the other answers to show that CONCRETE LINQ query resolution is not the same as IEnumerable<T>

and has nothing to do with immutability of anonymous type.

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 inferred accordingly, since the type of the result is actually

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

      

Also, if I try to resolve 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 get output again

"Allan"

      

However, in this case, my result type was overrated to

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

      

since it ToList()

creates a new collection. I can technically add and remove this collection at will, since the collection itself is quite ready for mutation.

Finally, this does not mean that the main object of the anonymous type is not immutable!

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

      

The error, regardless of the result, will be a list with the following error:

No assignment to property or index 'AnonymousType # 1.Name' - it is read-only

precisely because it is immutable.

+1


source







All Articles