c# - sintaxis - linq>#
Trampa de Variable Exterior (5)
@dtb es correcto (gran +1), pero es importante tener en cuenta que esto solo se aplica si el alcance del cierre se extiende fuera del ciclo. Por ejemplo:
var objects = new []
{
new { Name = "Bill", Id = 1 },
new { Name = "Bob", Id = 5 },
new { Name = "David", Id = 9 }
};
for (var i = 0; i < 10; i++)
{
var match = objects.SingleOrDefault(x => x.Id == i);
if (match != null)
{
Console.WriteLine("i: {0} match: {1}", i, match.Name);
}
}
Esto se imprimirá:
i: 1 match: Bill i: 5 match: Bob i: 9 match: David
ReSharper advertirá sobre "Acceso al cierre modificado", que puede ignorarse de forma segura en este caso.
¿Qué es exactamente la trampa de la variable externa? Explicación y ejemplos en C # son apreciados.
EDITAR: Incorporando el diktat de Jon Skeet :)
Algo como
foreach (var s in strings)
var x = results.Where(r => (r.Text).Contains(s));
No dará los resultados que espera porque los Contiene no se ejecutan para cada iteración. Sin embargo, la asignación de s a una variable temporal dentro del ciclo solucionará esto.
Este artículo que explica el concepto de cierres es útil:
http://en.wikipedia.org/wiki/Closure_(computer_science)
Además, este artículo es realmente bueno a partir de una implementación de C # más específica:
http://blogs.msdn.com/b/abhinaba/archive/2005/08/08/448939.aspx
De todos modos, el tl; lr es que el alcance variable es tan importante en delegados anónimos o expresiones lambda como lo es en cualquier otro lugar dentro de tu código; el comportamiento simplemente no es tan obvio.
La "Trampa de Variable Externa" ocurre cuando un desarrollador espera que el valor de una variable sea capturado por una expresión lambda o un delegado anónimo, cuando en realidad la variable se captura por sí misma.
Ejemplo:
var actions = new List<Action>();
for (var i = 0; i < 10; i++)
{
actions.Add(() => Console.Write("{0} ", i));
}
foreach (var action in actions)
{
action();
}
Posible resultado n. ° 1:
0 1 2 3 4 5 6 7 8 9
Posible resultado n. ° 2:
10 10 10 10 10 10 10 10 10 10
Si esperaba la salida n. ° 1, ha caído en la trampa de la variable externa. Obtienes la salida n. ° 2.
Fijar:
Declara una "variable interna" que se capturará repetidamente en lugar de la "variable externa" que se captura solo una vez.
var actions = new List<Action>();
for (var i = 0; i < 10; i++)
{
var j = i;
actions.Add(() => Console.Write("{0} ", j));
}
foreach (var action in actions)
{
action();
}
Para más detalles, ver también el blog de Eric Lippert .
Vale la pena observar que esta trampa también existió para los bucles foreach
pero se modificó desde C # 5.0, es decir, que los cierres de bucles foreach
ahora se cierran sobre una nueva copia de la variable de bucle cada vez. Entonces el siguiente código:
var values = new List<int>() { 100, 110, 120 };
var funcs = new List<Func<int>>();
foreach (var v in values)
funcs.Add(() => v);
foreach (var f in funcs)
Console.WriteLine(f());
Imprime 120 120 120
<C # 5.0 , pero 100 110 120
> = C # 5.0
Sin embargo, los bucles aún se comportan de la misma manera.