Buena pregunta. Como señala Reed, todos provienen principalmente de deferred execution (pero a diferencia de él, me parece un inconveniente. Solo estoy pensando por qué no se pueden llevar a cabo ejecuciones diferidas memorizando el estado). Aquí hay un par de ejemplos: todos son más o menos variantes del problema de ejecución diferida.
1) Soy demasiado vago para hacer algo a tiempo
LINQ se ejecuta sólo en la demanda.
Un error común que los novatos (yo mismo en el pasado incluido) hacen es no saber sobre la ejecución diferida. Por ejemplo, algo así como
var p = listOfAMillionComplexItems.OrderBy(x => x.ComplexProperty);
carreras en un santiamén, pero la clasificación actual no se completa hasta que enumerar la lista, es decir, la ejecución no se completa hasta que necesite el resultado de la ejecución. Para ejecutarlo, necesita algo como:
foreach(var item in p)...
//or
p.Count();
//or
p.ToList();
//etc
Véalos como consultas SQL. Si tiene
var query = from i in otherValues where i > 5 select i;
creo que es similar a la escritura
string query = "SELECT i FROM otherValues WHERE i > 5";
¿La última ejecución de una llamada a DB? No. Tiene que
Execute(query);
Es lo mismo aquí con Linq.
2) que viven en el presente
Sea cauteloso sobre variables dentro de las expresiones LINQ cambiándose más adelante.
Para estar seguro, haga una copia de seguridad de las variables primero y luego use la copia de seguridad en la consulta si la variable puede cambiar posteriormente antes de la ejecución real de la consulta.
From here:
decimal minimumBalance = 500;
var customersOver500 = from c in customers
where c.Balance > minimumBalance
select c;
minimumBalance = 200;
var customersOver200 = from c in customers
where c.Balance > minimumBalance
select c;
int count1 = customersOver500.Count();
int count2 = customersOver200.Count();
Supongamos que tenemos cuatro clientes con los siguientes saldos: 100, 300, 400 y 600. ¿Qué va a ser COUNT1 y Cont2? Ambos serán 3. El "customersOver500" hace referencia a la variable "minimumBalance", pero el valor no se obtiene hasta que los resultados de la consulta se repiten (a través de un ciclo for/each, una llamada ToList() o incluso un "Count() "llamar como se muestra arriba). En el momento en que se utiliza el valor para procesar la consulta, el valor de minimumBalance ya ha cambiado a 200, por lo que ambas consultas LINQ producen resultados idénticos (clientes con un saldo superior a 200).
3) Mi memoria es demasiado débil como para recordar los objetos de valor del pasado
Lo mismo que el anterior, el contexto de ser un poco diferente.
o esto desde el mismo sitio:
consideran este sencillo ejemplo de un método que utiliza LINQ a SQL para obtener una lista de los clientes:
public IEnumerable<Customer> GetCustomers()
{
using(var context = new DBContext())
{
return from c in context.Customers
where c.Balance > 2000
select c;
}
}
parece bastante inofensiva - hasta que obtenga una "ObjectDisposedException" cuando intente enumerar la colección. ¿Por qué? Porque LINQ en realidad no realiza la consulta hasta que intente y enumere los resultados. La clase DBContext (que expone la colección Clientes) se descarta cuando finaliza esta llamada. Una vez que intenta y enumera a través de la colección, se hace referencia a la clase DBContext.Customers y obtiene la excepción.
4) No trate de cogerme, todavía podría escabullo
try-catch no tiene sentido para una declaración si no se usa con prudencia.
En su lugar, el manejo de excepciones globales será mejor aquí.
try
{
wallet = bank.Select(c => Convert.ToInt32(""));
}
catch (Exception ex)
{
MessageBox.Show("Cannot convert bad int");
return;
}
foreach(int i in wallet)
//kaboom!
Ni que sale el mensaje de error correcto ni la función es dejar de fumar por return
.
5) No sólo estoy unpunctual, pero no aprenden de los errores, así
LINQ se ejecuta cada vez que enumerar sobre ellos. Así que no reutilices los enumerables de Linq.
Suponga que tiene un IQueryable
o IEnumerable
de regresar de una expresión LINQ. Ahora, al enumerar la colección, ¿se ejecutará la instrucción, pero solo una vez? No, cada vez que lo haces Esto me había mordido en el pasado. Si usted tiene:
var p = listOfAMillionComplexItems.OrderBy(x => x.ComplexProperty);
MessageBox.Show(p.Count().ToString()); //long process.
MessageBox.Show(p.Count().ToString()); //long process still.
Así que es mejor hacer
int i = p.Count(); //store in a variable to access count
//or better
var list = p.ToList(); //and start using list
6) Si usted no sabe usarme, puedo causar efectos secundarios!
Lo mismo que el anterior, solo para mostrar cómo la reutilización de enumerables de Linq puede causar un comportamiento no deseado.
vigile que no se hace la programación de efectos secundarios (ya que re-enumeración de LINQ es mucho más común) para dar un ejemplo salvaje,
p = bag.Select((t, i) => {if(i == 1) MessageBox.Show("Started off"); return t;});
Si enumerar el doble que saber qué cosa no deseada puede pasar.
7) Tenga cuidado con el fin estoy ejecuta cuando el encadenamiento
No sólo para las variables, incluso las funciones de Linq encadenados se pueden ejecutar con el fin diferente de lo que se esperaría normalmente (aunque el comportamiento es correcto) . No piense que es imperativo (paso a paso), piense cómo Linq puede posiblemente ejecutarlo.
Por ejemplo,
var d = Enumerable.Range(1, 100);
var f = d.Select(t => new Person());
f = f.Concat(f);
f.Distinct().Count() ??
¿Cuál será el número de los distintos en f
? Supongo que 100, no, pero es 200. El problema es que cuando se lleva a cabo la ejecución real de la lógica de concatenación, f
sigue siendo d.Select(t => new Person()
no ejecutado. Por lo tanto, esto cede efectivamente en
f = d.Select(t => new Person()).Concat(d.Select(t => new Person()));
que luego tiene 200 miembros distintos. Here's a link for the actual problem
8) Oye, en realidad somos más inteligentes de lo que piensas.
No es una advertencia en sí, pero hay muchos casos en que Linq puede superar su programa de estilo imperativo. Entonces, antes de optimizar, piense un poco, e incluso comparta.
El motivo por el que la ejecución diferida se ejecuta básicamente a pedido hace que Linq sea mucho más eficiente de lo que parece. El bloque iterador "cede" un elemento a la vez, según lo exigido, lo que le permite detener la ejecución cuando ya no es necesario. Aquí es una muy buena pregunta que detalla exactamente eso: Order of LINQ extension methods does not affect performance?
9) yo no estoy destinado a crujir número
Abuso de LINQ puede hacer que el código ineficiente, así como menos legible.
Para los algoritmos de cálculo numérico, Linq no es la herramienta adecuada, especialmente para grandes conjuntos de datos cuya complejidad puede escalar exponencialmente. A veces, solo dos bucles for serían suficientes. Lo mismo puede aplicarse al SQL sin formato cuando se compara con LINQ a SQL.
10) me contrata para el trabajo correcto
Preguntar LINQ a la mente de su oficina normal es mala opción de programación, algo que va en contra de la legibilidad.
Algunos por ejemplo:
medicines.Any(p =>
{
Console.WriteLine(p);
return false;
});
para un foreach en una enumerable.
o
medicines = medicines.Select(p =>
{
p.Id = 3;
return p;
});
sólo mala herramientas.
11) Depuración y perfiles pueden ser una pesadilla
Es difícil seguir lo que está sucediendo bajo el capó una expresión LINQ de VS
No
que su todo imposible, pero su poco de una tarea para depurar una consulta linq tan eficientemente como un código no lineal del propio VS. La creación de perfiles también se vuelve un poco más difícil debido a la naturaleza de la ejecución diferida. ¡Pero no debería impedir que nadie haga trivialmente uno o dos forros!
¡Un montón de advertencias relacionadas con la ejecución diferida más o menos! A ditto question here. Algunos leer relacionados en SO:
Examples on when not to use LINQ
Pros and Cons of LINQ (Language-Integrated Query)
What is the biggest mistake people make when starting to use LINQ?
drawbacks of linq
hmmm ... esta pregunta no era tan popular como lo había esperado que sería. Supongo que como mi respuesta obtuvo la mayor cantidad de votos, tendré que aceptarla como respuesta ... si eso cambiara, consideraría aceptar una respuesta diferente. – mezoid
esto no es una advertencia de Linq, es solo general * * lambda ** advertencia, una especie de implementación interrumpida específica solo para la era pre-C# 5. En C# 5 esto funciona como se esperaba. – nawfal