Where are the elements selected by LINQ stored?

3

I'm studying LINQ now and it hit me a question: A LINQ that runs without the methods ToList() or ToArray() returns a IEnumerable<> , right?

But a IEnumerable<> is not exactly a list / array , or is it? (I know it's just an interface that returns IEnumerator to Iterator and that it's on all lists, but that's another topic ...) Since an interface obviously can not be instantiated, where is it then stored the elements that LINQ selects? Does it do polymorphism and instantiate a List ? Example:

int[] numeros = new int[] { 0, 3, 10, 7, 14 };

// Na linha abaixo é aplicado polimorfismo e é criado uma lista?
// IEnumerable<int> numerosPares = new List<int>(); ?
IEnumerable<int> numerosPares = from n in numeros where (n % 2) == 0 select n;
foreach(int numero in numerosPares){
    Console.WriteLine(numero);
}
    
asked by anonymous 21.04.2015 / 05:32

3 answers

2
  

Since an interface obviously can not be instantiated, where is it then stored the elements that LINQ selects? Does it do polymorphism and instantiate a List ?

LINQ namespace methods return private implementations of the IEnumerable<T> interface. For example, the method Select<T> ( implementation here ) returns an instance of the private class WhereSelectEnumerableIterator<T> .

The OrderBy returns an instance of the class OrderedEnumerable

  

Where are the elements that LINQ selects stored?

As for storage - does not exist. Instances of IEnumerable<T> created using LINQ are monads - a very concept used in functional programming. In this case, the monad IEnumerable<T> does not represent the collection - it represents a computation, a code, that when executed will return a series of elements.

From the wiki :

  In functional programming, a monad is a structure that represents computations defined as the sequences of steps: a type with a monad structure defines what it means to chain operations, or in the functions of that type together.

In terms of status, this monad only contains the code needed to transform the original collection into another, and a pointer to the original collection.

    
21.04.2015 / 10:47
1

They are not stored because they do not yet exist.

When you write

IEnumerable<int> numerosPares = from n in numeros where (n % 2) == 0 select n;

Try to think of the following terms

Query[IEnumerable<int>] numerosPares = from n in numeros where (n % 2) == 0 select n;

What you are doing and creating a query . This query is not executed until you use it.

To force the execution of query , and as I said, you can use the methods ToList() or ToArray() (and then the results are brought to memory at once).

On the other hand, when you use query in a foreach(...) block, it executes it and traverses each element of the result one by one.

This is one of the reasons, for example, that ReSharper alerts you to the multiple use of the same query . If the data source changes between runs of query , the end result will change.

See the following example:

List<int> numeros = new List<int>(){2,4,6};
IEnumerable<int> numerosPares = from n in numeros where (n % 2) == 0 select n;
Console.WriteLine("Primeira execucao");
foreach(var n in numerosPares)
{
     Console.WriteLine(n); // Output 2,4,6
}
numeros.AddRange(new int[]{8,10,12});

Console.WriteLine("\nSegunda execucao");
foreach(var n in numerosPares)
{
     Console.WriteLine(n); // Output 2,4,6,8,10,12
}

(Code available on DotNetFiddle )

As you can see, the list that serves as the font changes between the runs and the printed numbers change as well.

At this moment, only the numeros list exists in memory.

So, if you want the result to be final (i.e., do not change between runs):

List<int> numeros = new List<int>(){2,4,6};
IEnumerable<int> numerosPares = (from n in numeros where (n % 2) == 0 select n).ToList();
Console.WriteLine("Primeira execucao");
foreach(var n in numerosPares)
{
    Console.WriteLine(n); // Output 2,4,6
}
numeros.AddRange(new int[]{8,10,12});

Console.WriteLine("\nSegunda execucao");
foreach(var n in numerosPares)
{
    Console.WriteLine(n); // Output 2,4,6
}

(Code available on DotNetFiddle )

In this example, the font list changes between runs but query has already been executed, meaning the printed numbers will no longer change even if the data source changes.

At this moment, in memory there are two lists: numeros and numerosPares .

    
21.04.2015 / 10:44
1

You've made a little mess. When it is said that an interface can not be instantiated, it does not mean that it can not have an instance. It can not be instantiated directly. Who will create this instance is a constructor of a class that implements the interface.

Ex:

public interface INomeavel
{
    string Nome { get; set; }
}

public class Produto : INomeavel
{
    public string Nome { get; set; }
    public decimal Preco { get; set; }
}

public class Local : INomeavel
{ 
    public string Nome { get; set; }
    public string Rua { get; set; }
}

In this case:

INomeavel produto1 = new Produto();
INomeavel local1 = new Local();

Who keeps the values from Nome to produto1 and local1 ? The very instance of each of the implementations. When casting for generic type INomeavel , you end up losing access to other members besides the interface members. But these members were not destroyed and can still be accessed by casting them back to the more specific type.

Anyway, when is this useful? In the example of your own question. Think of the interface as a way of saying that a type or group of types (those implementing the interface) will have AT LEAST those members defined by the interface.

So, where are the elements that LINQ selects? In the instance of any class that implements IEnumerable .

    
21.04.2015 / 17:03