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.
Yes it can. You just need to use the Cast<T> function to get it converted to a typed IEnumerable<T> .
IEnumerable in C# is an interface that defines one method, GetEnumerator which returns an IEnumerator interface. This allows readonly access to a collection then a collection that implements IEnumerable can be used with a for-each statement.
LINQ queries return a lazily evaluated IEnumerable<T> . The query is performed upon enumeration. Even if your source IEnumerable<T> had millions of records the above query would be instantaneous. Edit: Think of LINQ queries as creating a pipeline for your result rather than imperatively creating the result.
The principle difference is that IEnumerable will enumerate all of its elements all the time, while IQueryable will enumerate elements, or even do other things, based on a query. The query is an Expression (a data representation of .
IEnumerable e = ...; IEnumerable<object> e2 = e.Cast<object> (); Now e2 is an IEnumerable<T> and can work with all LINQ functions. You can also use LINQ's query comprehension syntax, which casts to the type of the range variable ( item in this example) if a type is specified:
The IEnumerable<T> interface is central to LINQ. All LINQ methods are extension methods to the IEnumerable<T> interface. That means that you can call any LINQ method on any object that implements IEnumerable<T>. You can even create your own classes that implement IEnumerable<T>, and those classes will instantly "inherit" all LINQ functionality!
How Where Works in LINQ? The main purpose of LINQ where is used to filter elements based on the conditions. It comes under the filtering operator category. It applies in both method and query syntax whereas method syntax requires the lambda expression and query syntax requires only the expression.
It applies in both method and query syntax whereas method syntax requires the lambda expression and query syntax requires only the expression. The LINQ Where is used to limit the number of records from select, update, delete statements. Given below are few examples of LINQ where code the list of elements in the collection.
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?
}
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With