views:

553

answers:

3

In another question on SO I answered with code like the one below and got a comment that the LINQ-query probably was evaluated in every iteration of the for/each. Is that true?

I know that LINQ-querys does not executes before its items is evaluated so it seems possible that this way to iterate the result can make it run on every iteration?

Dim d = New Dictionary(Of String, String)()    
d.Add("Teststring", "Hello")    
d.Add("1TestString1", "World")    
d.Add("2TestString2", "Test")    

For Each i As String In From e In d Where e.Value.StartsWith("W") Select e.Key
    MsgBox("This key has a matching value:" & i)    
Next
A: 

I looked up the FOR EACH...NEXT statement and it seems like Visual Basic evaluates the collection only once, before the loop begins, so it should not run the query on every iteration.

Number of Iterations. Visual Basic evaluates the collection only once, before the loop begins. If your statement block changes element or group, these changes do not affect the iteration of the loop.

Stefan
+1  A: 

I believe that it will run the first time it is reached and produce an enumeration that has all the matching values. What you will actually get back is an enumerator for that enumeration.

tvanfosson
+6  A: 

NO... in a foreach, the "GetEnumerator" is called only once (ever), and that is used going forward.

EDIT: I put a statement here about the result set being stored temporarily... that's only true for some cases... not this one, so I took it out.

EDIT: Please forgive this for being overly verbose... but I wanted to show you what is happening... so here's a Console app :)

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
     static void Main(string[] args)
     {
      foreach (string item in MyCustomEnumerator()
       .Where(item => item.StartsWith("abc")))
      {
   Console.WriteLine(item);
      }
     }

     static IEnumerable<string> MyCustomEnumerator()
     {
      Console.WriteLine(DateTime.Now);

      yield return "abc1";

      Console.WriteLine(DateTime.Now);

      yield return "abc2";

      Console.WriteLine(DateTime.Now);

      yield return "abc3";

      Console.WriteLine(DateTime.Now);

      yield return "xxx";
     }
    }
}

EDIT: This will result in a DateTime, then abc1, then a DateTime, then abc2, then a DateTime, then abc3, then a DateTime.

Timothy Khouri
Thanks. And pheuw. I used that way to iterate through LINQ results a lot in a recent project. ;)
Stefan