Strange behavior in linq C # with delayed execution

Hi, I have the following code causing strange behavior. The instance property of objects contained in IEnumerable created by linq to Objects is not updated in subsequent foreach operations. The foreach statement must include IEnumerable. Instead, the solution should list it earlier.

Although I found a solution, I did not see this documented anywhere in the books or in articles dealing with similar examples. Perhaps someone with intricate linq knowledge can explain this.

It took me a day to pinpoint the cause of the error and not easily debugged in a large application. Then I reproduced it in a much simpler environment, presented below.

public class MyClass { public int val ; } public class MyClassExtrax { public MyClass v1 { get; set; } public int prop1 { get; set; } } void Main() { List <MyClass> list1 = new List<MyClass>(); MyClass obj1 = new MyClass(); obj1.val = 10; list1.Add(obj1); MyClass obj2 = new MyClass(); obj2.val = 10; list1.Add(obj2); IEnumerable<MyClassExtrax> query1 = from v in list1 where v.val >= 0 select new MyClassExtrax{ v1=v , prop1=0 } ; //query1=query1.ToList(); solves the problem..but why is this needed..? foreach (MyClassExtrax fj in query1) { fj.v1.val = 40; fj.prop1 = 40; //property does not get updated.. } foreach (MyClass obj in list1) { Console.WriteLine("in list 1 value is {0} : ", obj.val); } foreach (MyClassExtrax obj in query1) { Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", obj.v1.val, obj.prop1); } } 

Output: in list 1, the value is 40:

in list 1, the value is 40:

in the MyClassExtra list, v1.val is 40, prop1 is 0

in the MyClassExtra list, v1.val is 40, prop1 is 0

As you can see, prop1 is not updated to 40. !!

+6
source share
1 answer

It is quite simple, well documented . This is due to the LINQ deferred execution function . This code

 IEnumerable<MyClassExtrax> query1 = from v in list1 where v.val >= 0 select new MyClassExtrax{ v1=v , prop1=0 } ; 

does not actually create objects. It simply creates an object, which when repeated actually creates the object. That is, you can think of query1 as a rule, which knows how to spit out objects when they are requested . Therefore, when you do this:

 foreach (MyClassExtrax fj in query1) { fj.v1.val = 40; fj.prop1 = 40; //property does not get updated.. } 

and then this:

 foreach (MyClassExtrax obj in query1) { Console.WriteLine("in MyClassExtra list v1.val is {0}, prop1 is {1} ", obj.v1.val, obj.prop1); } 

You are executing a rule to generate objects twice. That is, two different sequences of objects are created, and they are not divided between sequences. This is why you do not see updated values; references to two iterations of the sequence do not match.

However, when you call ToList and then move on to the resulting list, you have now produced only one sequence of objects, and this sequence of objects is obviously the same for two iterations.

+7
source

Source: https://habr.com/ru/post/896557/


All Articles