What is the difference between new Action() and a lambda?

52,586

Solution 1

The first version is effectively doing:

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

The problem you're running into is that the compiler has to know what kind of delegate (or expression tree) the lambda expression should be converted into. That's why this:

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

actually doesn't compile - it could be any delegate type with no parameters and either no return value or the same return type as _myMessage (which is presumably string). For instance, all of these are valid:

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

How could the C# compiler work out what type action was meant to be, if it were declared with var?

The simplest way to get round this when calling a method (for your Rhino Mocks example) is to cast:

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

Solution 2

Have you verified the second line actually compiles? It should not compile because C# does not support assigning a lambda expression to an implicitly typed variable (CS0815). This line will work in VB.Net though because it supports anonymous delegate creation (starting in VB 9.0).

The Rhino Mocks version does not compile for the same reason the second line should not compile. C# will not automatically infer a type for a lambda expression. Lambda expressions must be used in a context where it is possible to determine the delegate type they are intended to fulfill. The first line works great because the intended type is clear: Action. The Rhino Mocks version does not work because Delegate is more akin to an abstract delegate type. It must be a concrete delegate type such as Action or Func.

For a detailed discussion on this topic, you should read Eric Lippert's blog entries on the subject: http://blogs.msdn.com/ericlippert/archive/2007/01/11/lambda-expressions-vs-anonymous-methods-part-two.aspx

Solution 3

Action is a special type of delegate. So, if you use lambda will not understand which type you want to use since there are many (Action, Func,...).

To overcome this need for cast (which is slow in most of the cases) you can change the parameter of the base function from Delegate to Action:

IMethodOptions<T> Do(Action action);

This way you can use both statements and will not have any different:

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

If this is not possible then i suggest to use new Action(() => {}) instead of casting, it would be faster.

Please read the following link for more information on Action and Delegate: https://docs.microsoft.com/en-gb/dotnet/api/system.action?view=netframework-4.7.1#definition

Share:
52,586

Related videos on Youtube

Abhishek
Author by

Abhishek

I have been a senior developer with a focus on architecture, simplicity, and building effective teams for over ten years. As a director at Surge consulting I was involved in many operational duties and decisions and - in addition to software development duties - designed and implemented an interview processes and was involved in community building that saw it grow from 20 to about 350 developers and through an acquisition. I was then CTO setting up a dev shop at working closely with graduates of a coding bootcamp on both project work and helping them establish careers in the industry. Currently a Director of Engineering at findhelp.org your search engine for finding social services. I speak at conferences, have mentored dozens of software devs, have written popular articles, and been interviewed for a variety of podcasts and publications. I suppose that makes me an industry leader. I'm particularly interesting in companies that allow remote work and can check one or more of the following boxes: Product companies that help people in a non-trite manner (eg I'm not super interested in the next greatest way to get food delivered) Product companies that make developer or productivity tooling Funded startups that need a technical co-founder Functional programming (especially Clojure or Elixir) Companies trying to do something interesting with WebAssembly

Updated on May 11, 2020

Comments

  • Abhishek
    Abhishek about 4 years

    So when I write something like this

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

    Refactor Pro! Highlights this as a redundant delegate creation and allows me to to shorten it to

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

    And this usually works great. Usually, but not always. For example, Rhino Mocks has an extension method named Do:

    IMethodOptions<T> Do(Delegate action);
    

    Here, passing in the first version works, but the second doesn't. What exactly is going on under the covers here?

    • Aaron Hoffman
      Aaron Hoffman about 15 years
      Your second code block does not compile. I get this message "Cannot assign lambda expression to an implicitly-typed local variable". But, if I replace "var" with "Action" it does.
  • JaredPar
    JaredPar about 15 years
    VB.Net is able to get around this by generating delegate types on the fly based on the usage. Because VB already differentiates between void and non-void returning functions (sub and function) it makes the differentiation easier
  • MichaelGG
    MichaelGG about 15 years
    "How could the C# compiler work out what type action was meant to be, if it were declared with var?" Simple: function types should be first class structural types, not this named delegate stuff. And quoted code should be noted as such. But I guess that won't change now :).
  • H.B.
    H.B. over 12 years
    I think you need an additional pair of parentheses around the lambda to do such a cast.
  • Jon Skeet
    Jon Skeet over 12 years
    @H.B. Done - I'm always amazed at how many brackets you need when casting lambdas :)
  • H.B.
    H.B. over 12 years
    @JonSkeet: That is why i use new Action(() => ...) in those cases.