Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable<T> and .Where Linq method behaviour?

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.

like image 650
Fabrice Jumarie Avatar asked Oct 23 '18 14:10

Fabrice Jumarie


People also ask

Can I use LINQ with IEnumerable?

Yes it can. You just need to use the Cast<T> function to get it converted to a typed IEnumerable<T> .

What is IEnumerable in C# LINQ?

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.

Why does LINQ return IEnumerable?

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.

What is the difference between IEnumerable and IQueryable in C#?

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 .

How do I use IEnumerable with LINQ functions?

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:

What is the IEnumerable<T> interface?

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?

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.

What is the difference between LINQ where and method syntax?

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.


2 Answers

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 image 57
Tim Schmelter Avatar answered Oct 21 '22 08:10

Tim Schmelter


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();
like image 41
Salah Akbari Avatar answered Oct 21 '22 08:10

Salah Akbari