score:2

Accepted answer

your title says "...without reassigning to itself...": because a is a reference type, select() doesn't create copies of the items in the source collection. your lambda gets each actual object in turn. when it sets x.b = 0, it's acting on the original item that's still in the collection.

the more interesting question is why the arr1 and the arr2 code behave differently.

let's take a look at what select() returns:

var z = arr2.select(x => { x.b = 0; return x; });
arr2.tolist();

set a breakpoint on the second line there, and we find that this is type of z; this is the thing that is returned by arr2.select(x => { x.b = 0; return x; }). it's the same type of object you're calling tolist() on in the arr1 line:

system.linq.enumerable.selectlistiterator<consoleapp3.a, consoleapp3.a>

select() doesn't do much. it returns an object that's prepared to iterate over each item in arr2 in turn, set the b property of each item, and then return each item in turn.

it's ready to do that. but it hasn't done it, and it won't do it until you ask it to (lazy evaluation, as you suggest). let's try this:

var a = z.first();

that tells the selectlistiterator to evaluate the select() lambda for just the first item in arr2. that's all it does. now the first item in arr2 has b == 0, but the rest don't, because you didn't touch them yet. so let's touch all of them:

var b = z.tolist();

now the tolist() call will force the selectlistiterator to go through and execute your select() lambda expression for every item in arr2. you did that right away for arr1, which is why b was zero for every item in arr1. you never did it for arr2 in your code at all. the thing that does the work is not the select(), but the object returned by select(). for arr2, you discarded that object without enumerating it, so it never did the work.

and we now understand that arr2.tolist() didn't do anything: in the case of arr1, t was the act of calling tolist() on the result of the select() that applied the select()'s changes to arr1. if you had called arr1.tolist(); instead, that would have had no effect either. it would just create an exact copy of arr1, and if you didn't assign that to anything, it would just be discarded.

all of this is one reason why we never put side effects in linq expressions: you create effects which are baffling even in a minimal, highly simplified example created for a stackoverflow question. you don't need that in production code.

another reason is we never need to.

score:0

what you are doing is causing side effects in your select, that is instead of selecting data, you are assigning data "within" the select.

when you do this that code (the code that changes the data) gets executed whenever the collection gets enumerated, however neither arr1 nor arr2 are the collections getting enumerated here :

// here arr1 is not getting enumerated, what is getting enumerated is what 
// is returned by the select, and it is enumerated immdiately because the 
// tolist materializes it. this means that while the collection arr1 is 
// unchanged, you are changing the value of its members, hence why it shows in 
// your further console writelines
arr1.select(x=>{x.b=0;return x;}).tolist(); 

// here you have the same select, but you discard it! a select doesn't affect 
// the collection at all, arr2 is the same before and after the select, you 
// would have to call tolist on what was returned by the select, which is why 
// it worked on arr1 (because you chained the tolist, so it was applied to 
// what was returned by the select)
arr2.select(x=>{x.b=0;return x;});

// this does strictly nothing, you create a new list from arr2, which you do 
// not store
// arr2.tolist();

basically if you wanted to split the query for arr2 you would have to write it as such: var tmp = arr2.select(x=>{x.b=0;return x;}); tpm.tolist(); // call it on tmp not on arr2! arr2 was not changed, but tmp is what was returned by the select!

also note that overall, you should never do any of this, if you want to change every element of a collection, use a foreach, linq is there to shape and select data, not to modify it.

score:1

arr1.select(x=>{x.b=0;return x;}).tolist(); //enumerates the select, so it is executed
arr2.select(x=>{x.b=0;return x;}); //creates the query, it is not executed
arr2.tolist(); //enumerates the list you already have

Related Query

More Query from same tag