2009-04-19 9 views
30

Alors, quand j'écris quelque chose comme çaQuelle est la différence entre une nouvelle Action() et une lambda?

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

Refactor Pro! Met en évidence cela comme une création de délégué redondant et me permet de le raccourcir à

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

Et cela fonctionne généralement bien. Habituellement, mais pas toujours. Par exemple, Rhino Mocks a une méthode d'extension nommée Do:

IMethodOptions<T> Do(Delegate action); 

Ici, en passant dans les premiers travaux de version, mais la seconde ne fonctionne pas. Que se passe-t-il exactement sous les couvertures ici?

+4

Votre deuxième bloc de code ne compile pas. Je reçois ce message "Impossible d'affecter l'expression lambda à une variable locale implicitement typée". Mais, si je remplace "var" par "Action" c'est le cas. –

+1

Oui, vous avez raison, il ne peut pas être assigné à une variable implicitement typée, je l'éditerai. –

Répondre

47

La première version fait efficace:

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

Le problème que vous utilisez en est que le compilateur doit connaître le type de délégué (ou arbre d'expression) dans lequel l'expression lambda doit être convertie. Voilà pourquoi ceci:

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

ne fait compile pas - il pourrait être tout type de délégué sans paramètres ni aucune valeur de retour ou le même type de retour que _myMessage (ce qui est probablement string). Par exemple, tous ces éléments sont valables:

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

Comment le compilateur C# fonctionne quel type action était censé être, si elle était déclarée avec var?

La façon la plus simple de contourner cette méthode lorsque vous appelez un (pour votre exemple Rhino Mocks) est de lancer:

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

VB.Net est capable de contourner ce problème en générant des types de délégués à la volée en fonction de l'utilisation.Parce que VB différencie déjà les fonctions de retour vides et non-vides (sous et fonction), cela facilite la différenciation. – JaredPar

+4

"Comment le compilateur C# pouvait-il déterminer quelle action type devait être, si elle était déclarée avec var?" Simple: les types de fonctions doivent être des types structurels de première classe, pas ce type de délégué nommé. Et le code cité doit être noté comme tel. Mais je suppose que cela ne changera pas maintenant :). – MichaelGG

+1

Je pense que vous avez besoin d'une paire de parenthèses supplémentaire autour du lambda pour faire une telle distribution. –

8

Avez-vous vérifié que la deuxième ligne est réellement compilée? Il ne devrait pas compiler car C# ne supporte pas l'affectation d'une expression lambda à une variable implicitement typée (CS0815). Cette ligne fonctionnera dans VB.Net car elle prend en charge la création de délégué anonyme (à partir de VB 9.0).

La version de Rhino Mocks ne compile pas pour la même raison que la deuxième ligne ne devrait pas compiler. C# n'introduira pas automatiquement un type pour une expression lambda. Les expressions lambda doivent être utilisées dans un contexte où il est possible de déterminer le type de délégué qu'elles sont censées remplir. La première ligne fonctionne très bien car le type prévu est clair: Action. La version de Rhino Mocks ne fonctionne pas car Delegate est plus proche d'un type de délégué abstrait. Il doit s'agir d'un type de délégué concret tel que Action ou Func.

Pour une discussion détaillée sur ce sujet, vous devriez lire les entrées de blog d'Eric Lippert sur le sujet: http://blogs.msdn.com/ericlippert/archive/2007/01/11/lambda-expressions-vs-anonymous-methods-part-two.aspx

Questions connexes