Reference type vs value type
Classes and objects are reference types and when we assign them to a new object, we are just assigning the reference, not the actual object. And this is valid for C#, JavaScript, LINQ, the map function of es6 and everywhere.
They only return new values if it is a value type like primitive types like integers or structs. but if we are returning objects it always returns the reference and if we change the object properties it'll change in the source object as well. Unless we create a new object by using the new
keyword in the c# or we use the spread function in JavaScript ({...obj})
. It is important to understand that speared only works with one level and with primitive types so if the object has another object in it then that object would be copied by reference and changing that object will change the source object too.
To understand it a bit better consider the following code:
var item1 = new Item(1);
var item2 = new Item(2);
var temp = item1;
item1 = item2;
Let's assume that item1
is pointing to location 1000
in memory, and item2
is pointing to location 2000
in memory.
In line 3 when we assign item1
to temp
, we basically point the temp
object to the location that item1
is pointing. So now temp
is pointing to location 1000
in memory and that's it. Any further changes to item1
have nothing to do with temp
hence when in line 4 when we change the pointer of item1
from 1000
to 2000
it only changes the item1
pointer and does nothing to the temp
object. You can consider this kind of assignment as a snapshot of the references at the assignment line. Since in line 3, item1
was pointing to 1000
, temp
is pointing to 1000
and what happens after that doesn't affect temp
unless we assign temp
to item1
again to point it to the new location (2000
).
Don't mutate objects in LINQ Select
function and always return a new object. LINQ Select
is exactly like the JavaScript map
function. You need to create a new object if you don't want to mutate your source object. If the object is complicated you can deep copy it or add an interface like ICopy
which implements a method for returning a copy of the object this is more efficient than a deep copy because in a deep copy compiler has no idea about the properties at compile time and it uses reflection but if we implement ICopy
it gets compiled and it gets the benefits of optimisations that compiler does in compile time.