2010-02-09 12 views
13

Ho scoperto qualcosa di molto strano che spero di capire meglio.C# metodo gruppo strangenza

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))); 

che può essere riscritta come:

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

Come è possibile omettere il parametro di espressione lambda (i =>) e hanno ancora l'elemento corrente passato a Console.WriteLine?

Grazie per qualsiasi intuizione. -Keith

risposta

33

sta cercando un Action<T>. Quando si scrive

n.ForEach(Console.WriteLine); 

quello che avete qui è uno dei membri del gruppo di metodo di Console.WriteLine a giocare il ruolo di un Action<T>. Il compilatore cercherà il miglior sovraccarico di Console.WriteLine che mangia istanze di int. In effetti, utilizzerà il sovraccarico Console.WriteLine(int). Quindi utilizzerà questo sovraccarico per svolgere il ruolo di un Action<int>.

Per i dettagli su come è fatto, vedere §6.6 della specifica (conversioni di gruppo del metodo).

Tuttavia, quando si scrive

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

in realtà abbiamo una molto diversa Action<int> Nel primo caso, il Action<int> era Console.WriteLine(int). Qui, il Action<int> è equivalente a te aver scritto

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

e poi

n.ForEach(DoSomething); 

(Naturalmente, il compilatore deve passare attraverso lo stesso processo metodo di gruppo come descritto sopra per capire che cosa si intende per DoSomething).

Il punto è che nel primo caso il Action<int>èConsole.WriteLine(int). Tuttavia, nel secondo caso lo Action<int> è un middle man (l'espressione lambda) che a sua volta chiamerà Console.WriteLine(int).

+1

++ per immagini del compilatore "mangia" un sovraccarico di Console.WriteLine! –

+1

Molto ben articolato. Grazie! – Keith

+0

Bella spiegazione. –

2

Questo è meno confuso se si considera ciò che sta realmente accadendo.

Si sta passando un metodo a un argomento delegato. La maggior parte delle volte pensiamo ai delegati nel contesto degli eventi, ma possono anche essere parametri per i metodi. Non sembra strano quando un metodo viene aggiunto a un evento senza argomenti, è solo un aspetto insolito quando viene eseguito in questo contesto.

Prima di lambda, dovevi farlo tutto il tempo, ed era un tale dolore che non si sarebbe mai pensato di usare una libreria che assomigliava a LINQ. Con Lambdas, questo è più facile da fare, ma puoi sempre farlo anche alla vecchia maniera.

+0

C# 2.0 aveva la sintassi del delegato anonimo ('delegate() {}'), ma non era ancora abbastanza dolce zucchero sintattico e avrebbe fatto apparire LINQ piuttosto disordinato. – Gabe