score:2

Accepted answer

I've created a simple test console app.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;

namespace LinqPerformance
{
    class Program
    {
        static void Main(string[] args)
        {
            var data = Enumerable.Range(1, 100000000);

            for (int x = 0; x < 10; x++)
            {
                ExecuteMethods(data);
            }
        }

        private static void ExecuteMethods(IEnumerable<int> data)
        {
            Method1("linq collection", () =>
            {
                var collection = data.Where(d => d % 2 == 0);
                double count = 0;

                foreach (var c in collection)
                {
                    count += c;
                }
            });

            Method1("list collection", () =>
            {
                var collection = data.Where(d => d % 2 == 0).ToList();
                double count = 0;
                foreach (var c in collection)
                {
                    count += c;
                }
            });

            Method1("iterable collection", () =>
            {
                double count = 0;
                foreach (var c in data.Where(d => d % 2 == 0))
                {
                    count += c;
                }
            });
        }

        private static void Method1(string name, Action body)
        {
            Stopwatch s = new Stopwatch();

            s.Start();
            body();
            s.Stop();

            Console.WriteLine(name + ": " + s.Elapsed);
        }
    }
}

After running this I can see that the ToList() is the slowest. The other two approaches appear to be the same.

I suppose this is because the foreach is expanded to a

var enumerator = collection.GetEnumerator();

while(enumerator.MoveNext() )
{
    var c = enumerator.Current;
    count += c;
}

score:1

Performance is the same, whether you assign the Linq query to a variable, or call it directly in the For Each. In both case the iterator will be created once and the For Each loop will go through each item in the list once.

In the first code sample, the CType is not necessary though (actually I don't think it would work). You can simply do:

Dim collection = CellCollection.Where(Function(i) i.IsPending = True)
For Each item As Item In collection
    'Do something here
Next

But as I mentioned, assigning to a variable is not necessary. Having the Where clause on the For Each line will yield the same performance, and the code will be shorter and more readable.

score:0

The performance for both is the same. foreach will create the IEnumerable(Of T) then enumerate through it.

However, if you're concerned about performance, try:

Dim collection As IEnumerable(Of Item) _
    = CellCollection.Where(Function(i) i.IsPending)

For Each item As Item In collection 
    'Do something here
Next

It's possible that casting the IEnumerable(Of Item) to ItemCollection would cause it to enumerate (like ToArray or ToList). This will cause the collection to enumerate twice. Keeping it as IEnumerable ensures that the i.IsPending check happens during the enumeration of the For Each and not the CType().

The fastest solution would be to forgo LINQ altogether (LINQ statements, although readable, add some overhead).

For Each item As Item In CellCollection
    If Not item.IsPending Then
         Continue For
    End If
    'Do something here
Next

Related Articles