score:4

Accepted answer

The statements are, in this case, semantically the same as Select (and linq in general) uses deferred execution of delegates. It won't run any declared queries until the result is being materialised, and depending on how you write that query it will do it in proper sequence.

A very simple example to show that:

var list = new List<string>{"hello", "world", "example"};

Func<string, string> func = (s) => {
    Console.WriteLine(s);
    return s.ToUpper();
};

foreach(var item in list.Select(i => func(i)))
{
    Console.WriteLine(item);
}

results in

hello
HELLO
world
WORLD
example
EXAMPLE

score:4

In your first example, _executeFunc(row) will NOT be called first for each item in rows before your foreach loop begins. LINQ will defer execution. See This answer for more details.

The order of events will be:

  1. Evaluate the first item in rows
  2. Call executeFunc(row) on that item
  3. Call RunStoredProcedure(result)
  4. Repeat with the next item in rows

Now, if your code were something like this:

foreach (var result in rows.Select(row => _executeFunc(row)).ToList())
{                   
   RunStoredProcedure(result)
}

Then it WOULD run the LINQ .Select first for every item in rows because the .ToList() causes the collection to be enumerated.

score:2

Select uses deferred execution. This means that it will, in order:

  • take an item from rows
  • call _executeFunc on it
  • call RunStoredProcedure on the result of _executeFunc

And then it will do the same for the next item, until all the list has been processed.

score:3

In the top example, using Select will project the rows, by yielding them one by one.

So

foreach (var result in rows.Select(row => _executeFunc(row)))

is basically the same as

foreach(var row in rows)

Thus Select is doing something like this

for each row in source
   result = _executeFunc(row)
   yield result

That yield is passing each row back one by one (it's a bit more complicated than that, but this explanation should suffice for now).

If you did this instead

foreach (var result in rows.Select(row => _executeFunc(row)).ToList())

Calling ToList() will return a List of rows immediately, and that means _executeFunc() will indeed be called for every row, before you've had a chance to call RunStoredProcedure().

Thus what Resharper is suggesting is valid. To be fair, I'm sure the Jetbrains devs know what they are doing :)

score:1

The execution will be deferred meaning they will have the same exec


Related Articles