4

J'essaie de supprimer les 3 premiers éléments dans array avec la fonction d'extension LinQ Where.Linq Lorsque la fermeture locale de compteur différents résultats dans VS regarder

Voici un exemple:

var array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
var count = 3; 
var deletedTest1 = 0; 
var test1 = array.Where(x => ++deletedTest1 > count).ToList(); 
Console.WriteLine($"{{{String.Join(", ", test1)}}}"); 
var deletedTest2 = 0; 
var test2 = array.Where(x => ++deletedTest2 > count).AsEnumerable(); 
Console.WriteLine($"{{{String.Join(", ", test2)}}}"); 
var deletedTest3 = 0; 
var test3 = array.Where(x => ++deletedTest3 > count); 
Console.WriteLine($"{{{String.Join(", ", test3)}}}"); 
var deletedTest4 = 0; 
var test4 = array.Where(x => ++deletedTest4 > count).ToArray(); 
Console.WriteLine($"{{{String.Join(", ", test4)}}}"); 

Il fonctionne très bien et dans chaque cas j'ai { 4, 5, 6, 7, 8, 9 } résultat dans console.

Mais dans Visual Studio 2015 Mise à jour 3 dans test2 et test3 cas, j'ai un mauvais résultat:

enter image description here

Quelqu'un peut-il expliquer pourquoi tout est ok quand j'utilise .ToList() et .ToArray() et le mal dans d'autres cas ?

Est-ce un bug?

+4

Avec 'ToList()' et 'ToArray()' vous matérialisait les données à une liste/tableau. Les autres sont 'IEnumerable' qui promettent effectivement d'exécuter les données et vous les exécutez deux fois, une fois dans' Console.WriteLine' et une fois lorsque vous les affichez dans le débogueur. Il n'y a pas de bug. – DavidG

Répondre

2

La différence est due à l'exécution différée du lambda avec des effets secondaires. Vous devez être très prudent ici, car le lambda est évalué chaque fois que IEnumerable<T> produit par Where est énuméré, causant ses effets secondaires (c'est-à-dire incrémentant deletedTestX) encore et encore. Lorsque vous exécutez le programme, chacune de vos quatre séquences est énumérée une fois exactement. Pour les cas 1 et 4 l'énumération se passe à l'intérieur de ToList et ToArray, alors que pour les cas 2 et 3 cela se passe à l'intérieur de string.Join.

Lorsque vous ouvrez les résultats dans le débogueur, le contrôleur de la fenêtre de surveillance doit exécuter votre énumération afin de vous montrer les résultats. C'est la deuxième énumération de votre séquence, donc cela arrive avec les effets secondaires de la première énumération déjà appliquée. C'est pourquoi vous voyez des index erronés dans la fenêtre de débogage.

Vous pouvez reproduire ce comportement dans votre programme en imprimant chaque résultat deux fois:

var array = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
var count = 3; 
var deletedTest1 = 0; 
var test1 = array.Where(x => ++deletedTest1 > count).ToList(); 
Console.WriteLine("Test 1, deletedTest1={0}", deletedTest1); 
Console.WriteLine($"{{{String.Join(", ", test1)}}}"); 
Console.WriteLine("Test 1, deletedTest1={0}", deletedTest1); 
Console.WriteLine($"{{{String.Join(", ", test1)}}}"); 
var deletedTest2 = 0; 
var test2 = array.Where(x => ++deletedTest2 > count).AsEnumerable(); 
Console.WriteLine("Test 2, deletedTest2={0}", deletedTest2); 
Console.WriteLine($"{{{String.Join(", ", test2)}}}"); 
Console.WriteLine("Test 2, deletedTest2={0}", deletedTest2); 
Console.WriteLine($"{{{String.Join(", ", test2)}}}"); 
var deletedTest3 = 0; 
var test3 = array.Where(x => ++deletedTest3 > count); 
Console.WriteLine("Test 3, deletedTest3={0}", deletedTest3); 
Console.WriteLine($"{{{String.Join(", ", test3)}}}"); 
Console.WriteLine("Test 3, deletedTest3={0}", deletedTest3); 
Console.WriteLine($"{{{String.Join(", ", test3)}}}"); 
var deletedTest4 = 0; 
var test4 = array.Where(x => ++deletedTest4 > count).ToArray(); 
Console.WriteLine("Test 4, deletedTest4={0}", deletedTest4); 
Console.WriteLine($"{{{String.Join(", ", test4)}}}"); 
Console.WriteLine("Test 4, deletedTest4={0}", deletedTest4); 
Console.WriteLine($"{{{String.Join(", ", test4)}}}"); 

Demo.