C # Why are Except and Where Enumerable giving this weird result?

I once debugged this production error, and I desperately need help, and I'm also interested.

I simplified the logic of the code and added a printout for debugging:

int[] a = { 2,2,2 }; var b = a.Where(x => x==2); for(int i = 0; i < 3; i++) { var c = b.Where(x => x==i); Console.WriteLine("iter {0} before - B Count: {1}, C Count: {2}", i, b.Count(), c.Count()); if (c.Count() != b.Count()) b = b.Except(c); Console.WriteLine("iter {0} after - B Count: {1}, C Count: {2}", i, b.Count(), c.Count()); } Console.WriteLine("After Loop: B Count: {0}", b.Count()); 

Interesting (strange) output:

 iter 0 before - B Count: 3, C Count: 0 iter 0 after - B Count: 1, C Count: 0 iter 1 before - B Count: 1, C Count: 0 iter 1 after - B Count: 1, C Count: 0 iter 2 before - B Count: 0, C Count: 0 iter 2 after - B Count: 0, C Count: 0 After Loop: B Count: 1 

Question 1:

Why b.Count() == 0 in "iter 2 before". The only thing that happens between "iter 1 after and iter 2 before" is

 var c = b.Where(x => x==i); 

Why does this code even change?

Question 2:

Why b.Count() return to 1 after loop completion?

I am very grateful to everyone who helps me deal with this problem, thanks!

+5
source share
4 answers

Due to the lazy evaluation of linq, every Where and Except call is made every time you make a Count() call. It is important to note that the i value is not remembered; instead, it takes the current i value for all previous Where calls.

Why b.Count () == 0 in "iter 2 before"

At this moment, i already increasing to 2 . When you invoke an account, the entire linq query is evaluated using this value, which means that the previously added “Except` call will already delete everything!

Why does b.Count () return to 1 after loop completion?

The loop ends when i is 3. Now that you evaluate the entire linq query, none of the .Where(x => x==i) calls .Where(x => x==i) returns anything, which in turn means that none of the Except calls will not delete anything (other than duplicates).

+4
source

For 1 : "Why does this code even change?"

This is not true. Add (at the beginning of the cycle, before c ):

 Console.WriteLine("iter {0} before anything - B Count: {1}", i, b.Count()); 

b does not change.

For 2 : this is due to captured i . Change to:

 for(int tmp = 0 ; tmp < 3 ; tmp++) { int i = tmp; // ... } 

and try again. Now i has an area inside the loop and is fixed accordingly (the iterator variable in the for loop is limited outside the loop for complex reasons; in the foreach whether the iterator variable inside or inside the loop depends on the version of C # you are using)

+3
source

Except acts like its SQL counterpart because it eliminates duplicates. Because of this, it behaves as if it is called Distinct on your list.

So, when you first execute Except

 if (c.Count() != b.Count()) // c.Count() == 0, b.Count() == 3 b = b.Except(c); 

You get only unique elements b , of which only one.

+1
source

Enumerable.Except creates the specified difference between the two sequences, using the default equalizer to compare values. The set does not contain duplicates.

This is why the sequence contains only one 2 after Except .

 int[] onlyOneTwo = new[]{ 2,2,2 }.Except(Enumerable.Empty<int>()).ToArray(); 

Thus, an empty sequence does not delete anything except Except , which uses the set .

0
source

All Articles