2010-02-09 8 views
13

Descubrí algo muy extraño que espero comprender mejor.C# method group strangeness

var all = new List<int[]>{ 
       new int[]{1,2,3}, 
       new int[]{4,5,6}, 
       new int[]{7,8,9} 
       }; 

all.ForEach(n => n.ForEach(i => Console.WriteLine(i))); 

que se puede reescribir como:

... 
all.ForEach(n => n.ForEach(Console.WriteLine)); 

¿Cómo es posible dejar de lado el parámetro de expresión lambda (i =>) y aún así tener el elemento corriente que pasa a Console.Writeline?

Gracias por cualquier idea. -Keith

Respuesta

33

está buscando Action<T>. Cuando se escribe

n.ForEach(Console.WriteLine); 

lo que tenemos aquí es uno de los miembros del grupo método Console.WriteLine jugar el papel de un Action<T>. El compilador buscará la mejor sobrecarga de Console.WriteLine que come instancias de int. De hecho, usará la sobrecarga Console.WriteLine(int). Luego usará esta sobrecarga para jugar el rol de Action<int>.

Para obtener detalles sobre cómo se hace esto, consulte §6.6 de la especificación (conversiones de grupos de métodos).

Sin embargo, cuando se escribe

n.ForEach(i => Console.WriteLine(i)); 

en realidad tenemos una muy diferente Action<int> En el primer caso, el Action<int> era Console.WriteLine(int). Aquí, el Action<int> es equivalente a que haber escrito

public static void DoSomething(int i) { 
    Console.WriteLine(i); 
} 

y luego

n.ForEach(DoSomething); 

(Por supuesto, el compilador tiene que pasar por el mismo proceso del grupo método descrito anteriormente para averiguar lo que se entiende por DoSomething).

El punto es que en el primer caso el Action<int>esConsole.WriteLine(int). Sin embargo, en el segundo caso, el Action<int> es un intermediario (la expresión lambda) que a su vez llamará al Console.WriteLine(int).

+1

++ para imágenes del compilador "comiendo" una sobrecarga de Console.WriteLine! –

+1

Muy bien articulado. ¡Gracias! – Keith

+0

Buena explicación. –

2

Esto es menos confuso si se tiene en cuenta lo que realmente está sucediendo.

Está pasando un método a un argumento de delegado. La mayoría de las veces, pensamos en los delegados en el contexto de los eventos, pero también pueden ser parámetros de los métodos. No parece extraño cuando se agrega un método a un evento sin argumentos, simplemente se ve raro cuando se ejecuta en este contexto.

Antes de lambdas, tenías que hacer esto todo el tiempo, y era tan doloroso que uno nunca consideraría usar una biblioteca que se parecía a LINQ. Con Lambdas, esto es más fácil de hacer, pero siempre puedes hacerlo a la vieja usanza.

+0

C# 2.0 tenía la sintaxis delegada anónima ('delegate() {}'), pero aún no era lo suficientemente dulce como azúcar sintáctico y haría que LINQ se viera bastante desordenado. – Gabe