¿Por qué LINQ .Where (predicate) .First () es más rápido que .Primero (predicado)?

Estoy haciendo algunas pruebas de rendimiento y noté que una expresión LINQ como

result = list.First(f => f.Id == i).Property 

es más lento que

 result = list.Where(f => f.Id == i).First().Property 

Esto parece contrario a la intuición. Hubiera pensado que la primera expresión sería más rápida porque puede detener la iteración sobre la lista tan pronto como se satisfaga el predicado, mientras que yo hubiera pensado que la expresión .Where() podría iterar sobre toda la lista antes de llamar a .First() en el subconjunto resultante. Incluso si este último hace un cortocircuito, no debería ser más rápido que usar First directamente, pero lo es.

A continuación hay dos pruebas unitarias realmente simples que ilustran esto. Cuando se comstack con optimización en TestWhereAndFirst es aproximadamente 30% más rápido que TestFirstOnly en .Net y Silverlight 4. He intentado hacer que el predicado arroje más resultados, pero la diferencia de rendimiento es la misma.

¿ .First(fn) puede explicar por qué .First(fn) es más lento que .Where(fn).First() ? Veo un resultado contrario a la intuición similar con .Count(fn) comparación con .Where(fn).Count() .

 private const int Range = 50000; private class Simple { public int Id { get; set; } public int Value { get; set; } } [TestMethod()] public void TestFirstOnly() { List list = new List(Range); for (int i = Range - 1; i >= 0; --i) { list.Add(new Simple { Id = i, Value = 10 }); } int result = 0; for (int i = 0; i  f.Id == i).Value; } Assert.IsTrue(result > 0); } [TestMethod()] public void TestWhereAndFirst() { List list = new List(Range); for (int i = Range - 1; i >= 0; --i) { list.Add(new Simple { Id = i, Value = 10 }); } int result = 0; for (int i = 0; i  f.Id == i).First().Value; } Assert.IsTrue(result > 0); } 

Obtuve los mismos resultados: donde + primero fue más rápido que el primero.

Como notó Jon, Linq usa evaluación diferida por lo que el rendimiento debe ser (y es) ampliamente similar para ambos métodos.

Al buscar en Reflector, First utiliza un bucle foreach para iterar a través de la colección, pero Where tiene una variedad de iteradores especializados para diferentes tipos de colección (matrices, listas, etc.). Presumiblemente esto es lo que le da a Donde la pequeña ventaja.