匿名方法与Lambda表达式 [英] Anonymous methods vs. lambda expression

查看:97
本文介绍了匿名方法与Lambda表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

谁能在匿名方法和lambda表达式之间提供简洁的区分?



匿名方法的用法:

  private void DoSomeWork()
{
if(textBox1.InvokeRequired)
{
//textBox1.Invoke((动作)(()=> textBox1.Text = test)));
textBox1.Invoke((Action)delegate {textBox1.Text = test;});
}
}

是否只是将正常的lambda表达式强制转换为



我很清楚,像Follow这样的强类型委托

  UpdateTextDelegate mydelegate =新的UpdateTextDelegate(MethodName)

足以作为类型 System.Delegate 的参数,但是匿名方法的想法对我来说还很陌生。

解决方案

什么是 匿名方法?真的是匿名的吗?它有名字吗?所有的好问题,让我们从它们开始,并逐步处理lambda表达式。



执行此操作时:

  public void TestSomething()
{
Test(delegate {Debug.WriteLine( Test);});
}

实际上会发生什么?



编译器首先决定采用该方法的主体,即:

  Debug.WriteLine(测试); 

并将其分离为方法。



编译器现在必须回答两个问题:


  1. 该方法放在哪里?

  2. 该方法的签名应该是什么样?

第二个问题很容易回答。 delegate {部分回答了这一问题。该方法不带参数( delegate {之间没有任何值),并且由于我们不在乎其名称(因此,匿名部分),我们可以这样声明该方法:

  public void SomeOddMethod()
{
Debug.WriteLine( Test);
}

但是为什么要这么做呢?



让我们看看什么是委托人,例如 Action



委托人是,如果我们暂时忽略.NET中的委托实际上是多个单个委托的链接列表的事实,则是对两件事的引用(指针):


  1. 对象实例

  2. 该对象实例上的方法

因此,有了这些知识,第一段代码实际上可以这样重写:

  public TestSomething()
{
Test(new Action(this.SomeOddMethod));
}

private void SomeOddMethod()
{
Debug.WriteLine( Test);
}

现在,问题在于编译器无法知道什么 Test 实际上是对委托进行的,并且由于委托的一半是对要在其上调用方法的实例的引用,因此在上例中,此,我们不知道将引用多少数据。



例如,考虑上面的代码是否为一个真正巨大的物体的一部分,但只是暂时存在的物体。还应考虑 Test 会将该委托存储在可以长期保存的地方。这种长时间也将自己与该巨大物体的寿命联系在一起,并且长时间保持对该物体的引用,可能不好。



因此,编译器不仅可以创建方法,还可以创建一个类来保存该方法。这回答了第一个问题,我应该放在哪里?



上面的代码可以这样重写:

  public void TestSomething()
{
var temp = new SomeClass;
Test(new Action(temp.SomeOddMethod));
}

私有类SomeClass
{
private void SomeOddMethod()
{
Debug.WriteLine( Test);
}
}

对于本例来说,这是一个匿名方法确实是全部。



如果您开始使用局部变量,事情会变得更加繁琐,请考虑以下示例:

  public void Test()
{
int x = 10;
Test(delegate {Debug.WriteLine( x = + x);});
}

这是内幕发生的事情,或者至少是非常接近的事情:

  public void TestSomething()
{
var temp = new SomeClass;
temp.x = 10;
Test(new Action(temp.SomeOddMethod));
}

私有类SomeClass
{
public int x;

private void SomeOddMethod()
{
Debug.WriteLine( x = + x);
}
}

编译器创建一个类,将所有该方法需要进入该类,并重写对局部变量的所有访问权限,以访问匿名类型上的字段。



该类的名称和方法,有点奇怪,让我们问 LINQPad 会是什么:

  void Main()
{
int x = 10;
Test(delegate {Debug.WriteLine( x = + x);});
}

public void Test(Action action)
{
action();
}

如果我要求LINQPad输出此程序的IL(中间语言),我得到这个:

  // var temp = new UserQuery +<> c__DisplayClass1(); 
IL_0000:newobj UserQuery +<> c__DisplayClass1..ctor
IL_0005:stloc.0 // CS $<> 8__locals2
IL_0006:ldloc.0 // CS $< > 8__locals2

// temp.x = 10;
IL_0007:ldc.i4.s 0A
IL_0009:stfld UserQuery +<> c__DisplayClass1.x

// var action = new Action(temp。< Main> b__0);
IL_000E:ldarg.0
IL_000F:ldloc.0 // CS $<> 8__locals2
IL_0010:ldftn UserQuery +<> c__DisplayClass1。< Main> b__0
IL_0016:newobj System.Action..ctor

//测试(动作);
IL_001B:调用UserQuery.Test

测试:
IL_0000:ldarg.1
IL_0001:callvirt System.Action.Invoke
IL_0006:ret

<> c__DisplayClass1。< Main> b__0:
IL_0000:ldstr x =
IL_0005:ldarg.0
IL_0006:ldfld UserQuery +<> ; c__DisplayClass1.x
IL_000B:盒子System.Int32
IL_0010:调用System.String.Concat
IL_0015:调用System.Diagnostics.Debug.WriteLine
IL_001A:ret

<> c__DisplayClass1..ctor:
IL_0000:ldarg.0
IL_0001:调用System.Object..ctor
IL_0006:ret

在这里您可以看到该类的名称为 UserQuery +<> c__DisplayClass1 ,并且该方法的名称为< Main> b__0 。我在生成该代码的C#代码中进行了编辑,但是LINQPad除了上面的示例中的IL之外什么都不生成。



小于和大于符号是



因此,基本上这就是匿名方法。



p>

这是什么?

  Test(()=>调试.WriteLine( Test))); 

在这种情况下,它是相同的,它是产生匿名方法的捷径。



您可以用两种方式编写:

 ()=> {...这里的代码...} 
()=> ...这里的单个表达式...

在第一种形式中,您可以编写所有代码在正常的方法体内做。在第二种形式中,您可以编写一个表达式或语句。



但是,在这种情况下,编译器将处理以下内容:

 ()=> ... 

与以下方式相同:

 委托{...} 

仍然是匿名方法,只是()=> 语法是获取该方法的捷径。



因此,如果这是通向它的捷径,为什么我们要拥有它呢?



那么,为了增加它的用途,它会使生活变得容易一些,这就是LINQ。



请考虑以下LINQ语句:

  var客户=来自db.Customers 
中的customer,其中customer.Name == ACME
选择customer.Address;

此代码重写如下:

  var客户= 
db.Customers
.Where(customer => customer.Name == ACME)
.Select(customer => ; customer.Address);

如果要使用 delegate { ...} 语法,您必须使用 return ... 等来重写表达式,它们看起来更时髦。因此添加了lambda语法,使我们的程序员在编写上述代码时更加轻松。



那什么是表达式?



到目前为止,我还没有显示 Test 是如何定义的,但让我们定义 Test 用于以上代码:

  public void Test(动作)

这应该足够了。它说:我需要一个委托,它是Action类型的(不带参数,不返回值)。 p>

但是,微型soft还添加了另一种定义此方法的方法:

  public void Test(Expression< Func< ....> ; expr)

请注意,我在其中放了一部分,...。 部分,让我们回到 1



此代码与此调用配对:

  Test(()=> x + 10); 

实际上不会传递委托,也不会(立即)调用任何东西。取而代之的是,编译器会将这段代码改写为类似以下代码的 (但一点也不像):

  var操作数1 =新的VariableReferenceOperand( x); 
var操作数2 =新的ConstantOperand(10);
var expression = new AdditionOperator(operand1,opernd2);
测试(表达式);

基本上,编译器将构建 Expression< Func< ...> ;> 对象,包含对变量,文字值,使用的运算符等的引用,并将该对象树传递给方法。



为什么?



那么,考虑上面的 db.Customers.Where(...)部分。



如果不是将所有客户(及其所有数据)从数据库下载到客户端,然后遍历所有客户,找出哪个客户正确的名称等。代码实际上会要求数据库立即找到那个单一的,正确的客户?



这就是表达式的目的。实体框架,Linq2SQL或任何其他这样的支持LINQ的数据库层将采用该表达式,对其进行分析,将其拆开,并编写一个正确格式的SQL以对该数据库执行。



如果我们仍将其委托给包含IL的方法,这将永远不会。它之所以只能这样做是因为以下几点:


  1. lambda表达式中允许的语法适合表达式< Func< ...>> 是受限制的(没有语句等)。

  2. 没有大括号的lambda语法,它告诉编译器






所以,我们总结一下:


  1. 匿名方法实际上并非全部都是匿名方法,它们最终会成为命名类型,带有命名方法,只有您不必自己命名这些东西

  2. 很多隐藏在后台的编译器魔术使事情四处移动,因此您不必

  3. 表达式和委托是查看某些相同事物的两种方式

  4. 表达式用于希望了解代码作用和方式的框架。他们可以利用这些知识来优化流程(例如编写SQL语句)

  5. 代表用于仅关注是否能够调用方法的框架






脚注:


  1. ...。 部分用于这样的简单表达式,它表示从表达式中获得的返回值的类型。 ()=> ...简单表达式... 仅允许表达式,即返回值的内容,并且不能为多个语句。因此,有效的表达式类型是: Expression< Func< int>> ,基本上,该表达式是返回整数值的函数(方法)。



    请注意,返回值的表达式是 Expression< ...> 参数或类型的限制,但不是代表。如果 Test 的参数类型是 Action


    $,这是完全合法的代码b $ b

      Test(()=> Debug.WriteLine( Test))); 

    很明显, Debug.WriteLine( Test)不返回任何内容,但这是合法的。但是,如果方法 Test 需要一个 expression ,则不是,因为表达式必须返回一个值。



Can anyone provide a concise distinction between anonymous method and lambda expressions?

Usage of anonymous method:

private void DoSomeWork()
{
    if (textBox1.InvokeRequired)
    {
        //textBox1.Invoke((Action)(() => textBox1.Text = "test"));
        textBox1.Invoke((Action)delegate { textBox1.Text = "test"; });
    }
}

Is it only a normal lambda expression being cast to a strongly typed delegate or there is more of it undercover.

I'm well aware that a strongly typed delegate like follow

UpdateTextDelegate mydelegate = new UpdateTextDelegate(MethodName)

suffice as a parameter of type System.Delegate, but the idea of anonymous method is rather new to me.

解决方案

What is an anonymous method? Is it really anonymous? Does it have a name? All good questions, so let's start with them and work our way up to lambda expressions as we go along.

When you do this:

public void TestSomething()
{
    Test(delegate { Debug.WriteLine("Test"); });
}

What actually happens?

The compiler first decides to take the "body" of the method, which is this:

Debug.WriteLine("Test");

and separate out that into a method.

Two questions the compiler now has to answer:

  1. Where should I put the method?
  2. What should the signature of the method look like?

The second question is easily answered. The delegate { part answers that. The method takes no parameters (nothing between delegate and {), and since we don't care about its name (hence the "anonymous" part), we can declare the method as such:

public void SomeOddMethod()
{
    Debug.WriteLine("Test");
}

But why did it do all this?

Let's look at what a delegate, such as Action really is.

A delegate is, if we for a moment disregard the fact that delegates in .NET are actually linked list of multiple single "delegates", a reference (pointer) to two things:

  1. An object instance
  2. A method on that object instance

So, with that knowledge, the first piece of code could actually be rewritten as this:

public void TestSomething()
{
    Test(new Action(this.SomeOddMethod));
}

private void SomeOddMethod()
{
    Debug.WriteLine("Test");
}

Now, the problem with this is that the compiler has no way of knowing what Test actually does with the delegate it is given, and since one half of the delegate is a reference to the instance on which the method is to be called, this in the above example, we don't know how much data will be referenced.

For instance, consider if the above code was part of a really huge object, but an object that only live temporarily. Also consider that Test would store that delegate somewhere it would live for a long time. That "long time" would tie itself up to the life of that huge object as well, keeping a reference to that for a long time as well, probably not good.

So the compiler does more than just create a method, it also creates a class to hold it. This answers the first question, where should I put it?.

The code above can thus be rewritten as follows:

public void TestSomething()
{
    var temp = new SomeClass;
    Test(new Action(temp.SomeOddMethod));
}

private class SomeClass
{
    private void SomeOddMethod()
    {
        Debug.WriteLine("Test");
    }
}

That is, for this example, what an anonymous method is really all about.

Things get a bit more hairy if you start using local variables, consider this example:

public void Test()
{
    int x = 10;
    Test(delegate { Debug.WriteLine("x=" + x); });
}

This is what happens under the hood, or at least something very close to it:

public void TestSomething()
{
    var temp = new SomeClass;
    temp.x = 10;
    Test(new Action(temp.SomeOddMethod));
}

private class SomeClass
{
    public int x;

    private void SomeOddMethod()
    {
        Debug.WriteLine("x=" + x);
    }
}

The compiler creates a class, lifts all the variables that the method requires into that class, and rewrites all access to the local variables to be access to fields on the anonymous type.

The name of the class, and the method, are a bit odd, let's ask LINQPad what it would be:

void Main()
{
    int x = 10;
    Test(delegate { Debug.WriteLine("x=" + x); });
}

public void Test(Action action)
{
    action();
}

If I ask LINQPad to output the IL (Intermediate Language) of this program, I get this:

// var temp = new UserQuery+<>c__DisplayClass1();
IL_0000:  newobj      UserQuery+<>c__DisplayClass1..ctor
IL_0005:  stloc.0     // CS$<>8__locals2
IL_0006:  ldloc.0     // CS$<>8__locals2

// temp.x = 10;
IL_0007:  ldc.i4.s    0A 
IL_0009:  stfld       UserQuery+<>c__DisplayClass1.x

// var action = new Action(temp.<Main>b__0);
IL_000E:  ldarg.0     
IL_000F:  ldloc.0     // CS$<>8__locals2
IL_0010:  ldftn       UserQuery+<>c__DisplayClass1.<Main>b__0
IL_0016:  newobj      System.Action..ctor

// Test(action);
IL_001B:  call        UserQuery.Test

Test:
IL_0000:  ldarg.1     
IL_0001:  callvirt    System.Action.Invoke
IL_0006:  ret         

<>c__DisplayClass1.<Main>b__0:
IL_0000:  ldstr       "x="
IL_0005:  ldarg.0     
IL_0006:  ldfld       UserQuery+<>c__DisplayClass1.x
IL_000B:  box         System.Int32
IL_0010:  call        System.String.Concat
IL_0015:  call        System.Diagnostics.Debug.WriteLine
IL_001A:  ret         

<>c__DisplayClass1..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  ret         

Here you can see that the name of the class is UserQuery+<>c__DisplayClass1, and the name of the method is <Main>b__0. I edited in the C# code that produced this code, LINQPad doesn't produce anything but the IL in the example above.

The less-than and greater-than signs are there to ensure that you cannot by accident create a type and/or method that matches what the compiler produced for you.

So that's basically what an anonymous method is.

So what is this?

Test(() => Debug.WriteLine("Test"));

Well, in this case it's the same, it's a shortcut for producing an anonymous method.

You can write this in two ways:

() => { ... code here ... }
() => ... single expression here ...

In its first form you can write all the code you would do in a normal method body. In its second form you're allowed to write one expression or statement.

However, in this case the compiler will treat this:

() => ...

the same way as this:

delegate { ... }

They're still anonymous methods, it's just that the () => syntax is a shortcut to getting to it.

So if it's a shortcut to getting to it, why do we have it?

Well, it makes life a bit easier for the purpose of which it was added, which is LINQ.

Consider this LINQ statement:

var customers = from customer in db.Customers
                where customer.Name == "ACME"
                select customer.Address;

This code is rewritten as follows:

var customers =
    db.Customers
      .Where(customer => customer.Name == "ACME")
      .Select(customer => customer.Address");

If you were to use the delegate { ... } syntax, you would have to rewrite the expressions with return ... and so on, and they'd look more funky. The lambda syntax was thus added to make life easier for us programmers when writing code like the above.

So what are expressions?

So far I have not shown how Test has been defined, but let's define Test for the above code:

public void Test(Action action)

This should suffice. It says that "I need a delegate, it is of type Action (taking no parameters, returning no values)".

However, Microsoft also added a different way to define this method:

public void Test(Expression<Func<....>> expr)

Note that I dropped a part there, the .... part, let's get back to that 1.

This code, paired with this call:

Test(() => x + 10);

will not actually pass in a delegate, nor anything that can be called (immediately). Instead, the compiler will rewrite this code to something similar (but not at all like) the below code:

var operand1 = new VariableReferenceOperand("x");
var operand2 = new ConstantOperand(10);
var expression = new AdditionOperator(operand1, operand2);
Test(expression);

Basically the compiler will build up an Expression<Func<...>> object, containing references to the variables, the literal values, the operators used, etc. and pass that object tree to the method.

Why?

Well, consider the db.Customers.Where(...) part above.

Wouldn't it be nice if, instead of downloading all customers (and all their data) from the database to the client, looping through them all, finding out which customer has the right name, etc. the code would actually ask the database to find that single, correct, customer at once?

That's the purpose behind expression. The Entity Framework, Linq2SQL, or any other such LINQ-supporting database layer, will take that expression, analyze it, pick it apart, and write up a properly formatted SQL to be executed against the database.

This it could never do if we were still giving it delegates to methods containing IL. It can only do this because of a couple of things:

  1. The syntax allowed in a lambda expression suitable for an Expression<Func<...>> is limited (no statements, etc.)
  2. The lambda syntax without the curly brackets, which tells the compiler that this is a simpler form of code


So, let's summarize:

  1. Anonymous methods are really not all that anonymous, they end up as a named type, with a named method, only you do not have to name those things yourself
  2. It's a lot of compiler magic under the hood that moves things around so that you don't have to
  3. Expressions and Delegates are two ways to look at some of the same things
  4. Expressions are meant for frameworks that wants to know what the code does and how, so that they can use that knowledge to optimize the process (like writing a SQL statement)
  5. Delegates are meant for frameworks that are only concerned about being able to call the method


Footnotes:

  1. The .... part for such a simple expression is meant for the type of return value you get from the expression. The () => ... simple expression ... only allows expressions, that is, something that returns a value, and it cannot be multiple statements. As such, a valid expression type is this: Expression<Func<int>>, basically, the expression is a function (method) returning an integer value.

    Note that the "expression that returns a value" is a limit for Expression<...> parameters or types, but not of delegates. This is entirely legal code if the parameter type of Test is an Action:

    Test(() => Debug.WriteLine("Test"));
    

    Obviously, Debug.WriteLine("Test") doesn't return anything, but this is legal. If the method Test required an expression however, it would not be, as an expression must return a value.

这篇关于匿名方法与Lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆