Where are the elements selected by LINQ stored?

Asked

Viewed 133 times

3

I’m studying LINQ now and 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? ( As far as I know he’s only one interface that returns a IEnumerator to the Iterator and that he’s on every list, but that’s another topic... ) Since a interface obviously cannot be instantiated, where then is stored the elements that LINQ selects? It makes polymorphism and instantiation 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);
}

3 answers

2


Since an interface obviously cannot be instantiated, where then is stored the elements that LINQ selects? It makes polymorphism and instantiation a List?

LINQ namespace methods return private interface implementations IEnumerable<T>. 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 the elements selected by LINQ are stored?

Storage - does not exist. Instances of IEnumerable<T> created using LINQ are monads - a concept much used in functional programming. In this case, the Monad IEnumerable<T> does not represent the collection - represents a computation, a code, which when executed will return a series of elements.

Wiki:

In Functional Programming, a Monad is a Structure that represents computations defined as sequences of Steps: a type with a Monad Structure defines what it Means to chain Operations, or Nest 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.

1

They don’t stay stored because they don’t exist yet.

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 are you doing and create 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 at that moment the results are brought to memory at once).

On the other hand, when using the query in a block foreach(...), running it and going through each element of the result one by one.

This is one of the reasons, for example, that Resharper warns of multiple use of it query. If the data source changes between 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 source changes between the executions and the printed numbers change as well.

At this time, in memory there is only the list numeros.

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

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 list that serves as the source changes between executions but the query has already been executed, meaning printed numbers will no longer change even if the data source changes.

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

1

You made a little mess. When it is said that an interface cannot be instantiated, it does not mean that it cannot have an instance. It cannot 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 that case:

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

Who keeps the values of Nome for produto1 and local1? The very instance of each of the implementations. When casting for the generic type INomeavel, you end up losing access to other members besides the interface members. But these members have not been destroyed and can still be accessed by doing the cast 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 a group of types (those that implement the interface) will AT LEAST have those members defined by the interface.

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

Browser other questions tagged

You are not signed in. Login or sign up in order to post.