问题
I thought I know everything about IEnumerable<T>
but I just met a case that I cannot explain. When we call .Where linq method on a IEnumerable
, the execution is deferred until the object is enumerated, isn't it?
So how to explain the sample below :
public class CTest
{
public CTest(int amount)
{
Amount = amount;
}
public int Amount { get; set; }
public override string ToString()
{
return $"Amount:{Amount}";
}
public static IEnumerable<CTest> GenerateEnumerableTest()
{
var tab = new List<int> { 2, 5, 10, 12 };
return tab.Select(t => new CTest(t));
}
}
Nothing bad so far!
But the following test gives me an unexpected result although my knowledge regarding IEnumerable<T>
and .Where
linq method :
[TestMethod]
public void TestCSharp()
{
var tab = CTest.GenerateEnumerableTest();
foreach (var item in tab.Where(i => i.Amount > 6))
{
item.Amount = item.Amount * 2;
}
foreach (var t in tab)
{
var s = t.ToString();
Debug.Print(s);
}
}
No item from tab will be multiplied by 2. The output will be : Amount:2 Amount:5 Amount:10 Amount:12
Does anyone can explain why after enumerating tab, I get the original value.
Of course, everything work fine after calling .ToList()
just after calling GenerateEnumerableTest()
method.
回答1:
var tab = CTest.GenerateEnumerableTest();
This tab
is a LINQ query that generates CTest
instances that are initialized from int
-values which come from an integer array which will never change. So whenever you ask for this query you will get the "same" instances(with the original Amount
).
If you want to "materialize" this query you could use ToList
and then change them.
Otherwise you are modifying CTest
instances that exist only in the first foreach
loop. The second loop enumerates other CTest
instances with the unmodified Amount
.
So the query contains the informations how to get the items, you could also call the method directly:
foreach (var item in CTest.GenerateEnumerableTest().Where(i => i.Amount > 6))
{
item.Amount = item.Amount * 2;
}
foreach (var t in CTest.GenerateEnumerableTest())
{
// now you don't expect them to be changed, do you?
}
回答2:
Like many LINQ operations, Select is lazy and use deferred execution so your lambda expression is never being executed, because you're calling Select
but never using the results. This is why, everything work fine after calling .ToList()
just after calling GenerateEnumerableTest()
method:
var tab = CTest.GenerateEnumerableTest().ToList();
来源:https://stackoverflow.com/questions/52951497/ienumerablet-and-where-linq-method-behaviour