c# - usar - temas en grupos de facebook
¿Hay algún beneficio al usar un grupo de métodos C#si está disponible? (7)
Cuando se trata de algo como una List<string>
puede escribir lo siguiente:
list.ForEach(x => Console.WriteLine(x));
o puedes usar un grupo de métodos para hacer la misma operación:
list.ForEach(Console.WriteLine);
Prefiero la segunda línea de código porque me parece más limpio, pero ¿hay algún beneficio para esto?
Bueno, vamos a echar un vistazo y ver qué pasa.
static void MethodGroup()
{
new List<string>().ForEach(Console.WriteLine);
}
static void LambdaExpression()
{
new List<string>().ForEach(x => Console.WriteLine(x));
}
Esto se compila en la siguiente IL.
.method private hidebysig static void MethodGroup() cil managed
{
.maxstack 8
L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
L_0005: ldnull
L_0006: ldftn void [mscorlib]System.Console::WriteLine(string)
L_000c: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
L_0011: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
L_0016: ret
}
.method private hidebysig static void LambdaExpression() cil managed
{
.maxstack 8
L_0000: newobj instance void [mscorlib]System.Collections.Generic.List`1<string>::.ctor()
L_0005: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
L_000a: brtrue.s L_001d
L_000c: ldnull
L_000d: ldftn void Sandbox.Program::<LambdaExpression>b__0(string)
L_0013: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
L_0018: stsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
L_001d: ldsfld class [mscorlib]System.Action`1<string> Sandbox.Program::CS$<>9__CachedAnonymousMethodDelegate1
L_0022: call instance void [mscorlib]System.Collections.Generic.List`1<string>::ForEach(class [mscorlib]System.Action`1<!0>)
L_0027: ret
}
Observe cómo el enfoque de grupo de métodos crea un delegado de Action<T>
para un uso único y el enfoque de expresión lambda crea un campo de delegado anónimo oculto y realiza una inicialización en línea si es necesario. Note la instrucción IL_000a
en IL_000a
.
Como otros han señalado, hay una capa innecesaria adicional de indirección inducida por la lambda. Sin embargo, también hay diferencias sutiles de lenguaje. Por ejemplo, en C # 3, la inferencia de tipo genérico funciona de manera diferente en M(F)
que en M(x=>F(x))
cuando se intenta realizar una inferencia de tipo de retorno.
Para más detalles ver:
y el seguimiento:
http://blogs.msdn.com/b/ericlippert/archive/2008/05/28/method-type-inference-changes-part-zero.aspx
Creo que hay un beneficio. En el primer caso, está creando un método anónimo que llama la función Console.Writeline(string)
, mientras que en el otro caso, simplemente está pasando la referencia a la función existente.
Hay un nivel adicional de indirección cuando se usa la expresión lambda. Con una expresión de no cierre como esa, simplemente tendrá un método adicional de llamada intermedia, como lo mencionaron otros.
Sin embargo, hay algunas diferencias interesantes. En el segundo caso, se crea una nueva instancia de delegado en cada llamada. Para el primero, el delegado se crea una vez y se almacena en caché como un campo oculto, por lo que si llama mucho, ahorrará en las asignaciones.
Además, si introduce una variable local en la expresión lambda, se convierte en un cierre y, en lugar de que solo se genere un método local, se creará una nueva clase para contener esta información, lo que significa una asignación adicional allí.
No hay beneficios tangibles más que hacerlo más placentero para las personas que gustan de los grupos de métodos, y molestar a las personas que no les gustan [si eso le complace.] Además, hace que su código sea incompatible con compiladores anteriores.
-Oisin
Personalmente, también prefiero el segundo porque es menos confuso para depurar, pero en este caso creo que es solo una cuestión de estilo ya que ambos terminan haciendo lo mismo.
Sí; Lo primero en realidad puede causar una llamada interina, extra, innecesaria; pasar x
en un método que simplemente llama Console.WriteLine(x);
No necesita hacer el primero porque Console.WriteLine ya es un método que coincide con la firma que ForEach está buscando.