2009-04-19 15 views
30

Więc kiedy piszę coś takiegoJaka jest różnica między nową Action() i lambda?

Action action = new Action(()=>_myMessage = "hello"); 

Refactor Pro! Podkreśla to jako tworzenie redundantnych delegatów i pozwala je skrócić do

Action action =() => _myMessage="hello"; 

A to zwykle działa świetnie. Zazwyczaj, ale nie zawsze. Na przykład, Rhino Mocks ma metodę rozszerzenia nazwie zrobić:

IMethodOptions<T> Do(Delegate action); 

Tu, przechodząc w pierwszych prac wersji, ale drugi nie. Co dokładnie dzieje się tutaj pod kołdrą?

+4

Twój drugi blok kodu nie skompilować. Otrzymuję komunikat "Nie można przypisać wyrażenia lambda do niejawnie wpisanej zmiennej lokalnej". Ale jeśli zastąpię "var" przez "Akcja", robi to. –

+1

Tak, masz rację, nie można go przypisać do niejawnie wpisanej zmiennej, będę ją edytować. –

Odpowiedz

47

Pierwsza wersja jest skutecznie robi:

Action tmp =() => _myMessage = "hello"; 
var action = new Action(tmp); 

problemu używasz do jest kompilator musi wiedzieć, jakiego rodzaju delegata (lub drzewa wyrażeń) należy przekonwertować na wyrażenie lambda. Dlatego to:

var action =() => _myMessage="hello"; 

rzeczywiście nie kompiluje - może to być dowolny typdelegat bez parametrów i nie zwraca wartości obu tego samego typu lub powrotnego jako _myMessage (co jest przypuszczalnie string). Na przykład, wszystkie z nich są ważne:

Action action =() => _myMessage="hello"; 
Func<string> action =() => _myMessage="hello"; 
MethodInvoker action =() => _myMessage="hello"; 
Expression<Action> =() => _myMessage="hello"; 
// etc 

Jak można kompilator C# wypracować jakiego typu action miało być, jeżeli zostały zgłoszone z var?

Najprostszym sposobem, aby ominąć to podczas wywoływania metody (dla Rhino kpi przykład) jest obsada:

methodOptions.Do((Action) (() => _myMessage = "hello")); 
+3

VB.Net jest w stanie ominąć to poprzez generowanie typów delegatów w locie na podstawie użycia.Ponieważ VB już rozróżnia między funkcjami zwracającymi i nieważnymi (sub i funkcja), ułatwia to rozróżnianie. – JaredPar

+4

"W jaki sposób kompilator C# mógłby ustalić, jakie działanie miało mieć działanie, jeśli zostało zadeklarowane za pomocą var?" Prosty: typy funkcji powinny być pierwszorzędnymi typami strukturalnymi, a nie nazwanymi obiektami delegowanymi. Cytowany kod należy odnotować jako taki. Ale myślę, że to się teraz nie zmieni :). – MichaelGG

+1

Myślę, że potrzebujesz dodatkowej pary nawiasów wokół lambdy, aby wykonać taką obsadę. –

8

Czy zweryfikowałeś, że druga linia rzeczywiście się kompiluje? Nie powinno się kompilować, ponieważ C# nie obsługuje przypisywania wyrażenia lambda do niejawnie wpisanej zmiennej (CS0815). Ta linia będzie działać w VB.Net, ponieważ obsługuje anonimowe tworzenie delegatów (począwszy od wersji VB 9.0).

Wersja Rhino Mocks nie kompiluje się z tego samego powodu, dla którego druga linia nie powinna się kompilować. C# nie będzie automatycznie wyprowadzać typu dla wyrażenia lambda. Wyrażenia lambdy muszą być używane w kontekście, w którym możliwe jest określenie typu przekazanego, który ma spełniać. Pierwsza linia działa świetnie, ponieważ zamierzony typ jest jasny: Action. Wersja Rhino Mocks nie działa, ponieważ Delegat jest bardziej zbliżony do abstrakcyjnego typu delegata. Musi to być konkretny typ delegata, taki jak Action lub Func.

Szczegółową dyskusję na ten temat, należy przeczytać wpisy Eric Lippert w tej sprawie: http://blogs.msdn.com/ericlippert/archive/2007/01/11/lambda-expressions-vs-anonymous-methods-part-two.aspx

Powiązane problemy