2010-09-29 10 views
9

Zauważyłem kilka przykładów rzeczy, które działają i nie działają podczas pracy z funkcjami lambda i anonimowymi delegatami w języku C#. Co tu się dzieje?Jak działają typing delikates/lambda i koercja?

class Test : Control { 
    void testInvoke() { 
     // The best overloaded method match for 'Invoke' has some invalid arguments 
     Invoke(doSomething); 

     // Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type 
     Invoke(delegate { doSomething(); }); 

     // OK 
     Invoke((Action)doSomething); 

     // OK 
     Invoke((Action)delegate { doSomething(); }); 

     // Cannot convert lambda expression to type 'System.Delegate' because it is not a delegate type 
     Invoke(() => doSomething()); 

     // OK 
     Invoke((Action)(() => doSomething())); 
    } 

    void testQueueUserWorkItem() { 
     // The best overloaded method match for 'QueueUserWorkItem' has some invalid arguments 
     ThreadPool.QueueUserWorkItem(doSomething); 

     // OK 
     ThreadPool.QueueUserWorkItem(delegate { doSomething(); }); 

     // The best overloaded method match for 'QueueUserWorkItem' has some invalid arguments 
     ThreadPool.QueueUserWorkItem((Action)doSomething); 

     // No overload for 'doSomething' matches delegate 'WaitCallback' 
     ThreadPool.QueueUserWorkItem((WaitCallback)doSomething); 

     // OK 
     ThreadPool.QueueUserWorkItem((WaitCallback)delegate { doSomething(); }); 

     // Delegate 'WaitCallback' does not take '0' arguments 
     ThreadPool.QueueUserWorkItem(() => doSomething()); 

     // OK 
     ThreadPool.QueueUserWorkItem(state => doSomething()); 
    } 

    void doSomething() { 
     // ... 
    } 
} 

Cóż, to dużo przykładów. Chyba moje pytania są następujące:

  1. Dlaczego Invoke zawsze odmówić funkcję lambda lub anonimowy delegata, jeszcze ThreadPool.QueueUserWorkItem robi dobrze?

  2. Co do cholery robi "Nie można przekonwertować metody anonimowej do typu" System.Delegate ", ponieważ nie jest to typ delegata" oznacza tak?

  3. Dlaczego ThreadPool.QueueUserWorkItem akceptuje anonimowy delegat bez parametrów, ale bez wyrażenia lambda bez parametrów?

Odpowiedz

9
  1. ThreadPool.QueueUserWorkItem ma specyficzny powierzać jej podpisania; Wywołanie ma tylko Delegate. Wyrażenia lambdy i metody anonimowe można konwertować tylko na określony typ delegatów.

  2. To tylko zły komunikat o błędzie. Oznacza to, "Nie wiem dokładnie, jakiego rodzaju delegata chcesz przekonwertować."

  3. Używasz anonimowej metody bez listy parametrów w ogóle, która może zostać przekonwertowana na dowolny typ uczestnika, który nie używa parametrów out/ref. Jeśli wypróbujesz delegate() { ... } (tj. Jawnie pustą listę parametrów), to nie będzie działać. Ta "nie zależy mi na parametrach" zdolność anonimowych metod jest taka, jaką mają one w wyrażeniach lambda.

To najłatwiej wykazać wszystko to w kontekście prostych zadań, IMO:

// Doesn't work: no specific type 
Delegate d =() => Console.WriteLine("Bang"); 

// Fine: we know the exact type to convert to 
Action a =() => Console.WriteLine("Yay"); 

// Doesn't work: EventHandler isn't parameterless; we've specified 0 parameters 
EventHandler e1 =() => Console.WriteLine("Bang"); 
EventHandler e2 = delegate() { Console.WriteLine("Bang again"); }; 

// Works: we don't care about parameter lists 
EventHandler e = delegate { Console.WriteLine("Lambdas can't do this"); }; 
+4

I pomyślałem, zrozumiałem delegatów i lambdy. Byłem tak głupi. – recursive

+0

Dobra rzecz jak zawsze, +1 dla "... jest jedyną cechą jaką mają wyrażenia lambda". – Ani

+1

@recursive, jeśli chcesz dowiedzieć się więcej, przeczytaj o drzewach wyrażeń. Interesującą rzeczą w wyrażeniach lambda jest to, że naprawdę są one _expressions_, z bogatym opisowym modelem tego, co jest wyrażane, a nie tylko syntaktycznym skrótem dla przekazywania logiki do parametru delegata. To właśnie pozwala magii, takiej jak LINQ, na SQL. http://msdn.microsoft.com/en-us/library/bb397951.aspx –