web-dev-qa-db-fra.com

Quelle est la différence entre une nouvelle Action () et une lambda?

Alors quand j'écris quelque chose comme ça

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

Refactor Pro! Souligne 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 très bien. Habituellement, mais pas toujours. Par exemple, Rhino Mocks a une méthode d'extension nommée Do:

IMethodOptions<T> Do(Delegate action);

Ici, le passage de la première version fonctionne, mais pas la seconde. Que se passe-t-il exactement sous les couvertures ici?

41
George Mauer

La première version fait effectivement:

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

Le problème que vous rencontrez est que le compilateur doit savoir dans quel type de délégué (ou arborescence d'expression) l'expression lambda doit être convertie. Voilà pourquoi cela:

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

ne compile pas en fait - il pourrait être n'importe quel type délégué sans paramètre et sans valeur de retour ou avec le même type de retour que _myMessage (qui est vraisemblablement string). Par exemple, tous ces éléments sont valides:

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

Comment le compilateur C # pourrait-il déterminer quel type action était censé être, s'il était déclaré avec var?

La façon la plus simple de contourner cela lors de l'appel d'une méthode (pour votre exemple Rhino Mocks) est de lancer:

methodOptions.Do((Action) (() => _myMessage = "hello"));
60
Jon Skeet

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

La version de Rhino Mocks ne compile pas pour la même raison que la deuxième ligne ne doit pas être compilée. C # ne déduira 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 s'apparente davantage à 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

9
JaredPar

L'action est un type spécial de délégué. Donc, si vous utilisez lambda, vous ne comprendrez pas quel type vous voulez utiliser car il y en a beaucoup (Action, Func, ...) .

Pour surmonter ce besoin de cast (qui est lent dans la plupart des cas) , vous pouvez changer le paramètre de la fonction de base de Délégué en Action:

IMethodOptions<T> Do(Action action);

De cette façon, vous pouvez utiliser les deux instructions et vous n'aurez aucune différence:

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

Si ce n'est pas possible, je suggère d'utiliser new Action (() => {}) au lieu de lancer, ce serait plus rapide.

Veuillez lire le lien suivant pour plus d'informations sur Action et Délégué: https://docs.Microsoft.com/en-gb/dotnet/api/system.action?view=netframework-4.7.1#definition =

1
Avjol Sakaj