委托关键字与Lambda表示法


Answers:


140

简短的回答:不。

更长的答案可能不相关:

  • 如果将lambda分配给委托类型(例如FuncAction),则将获得一个匿名委托。
  • 如果将lambda分配给Expression类型,则将获得一个表达式树,而不是一个匿名委托。然后可以将表达式树编译为匿名委托。

编辑:这是表达式的一些链接。

  • System.Linq.Expression.Expression(TDelegate)(从此处开始)。
  • 具有委托的Linq内存(例如System.Func)使用System.Linq.Enumerable。带有表达式的Linq to SQL(以及其他任何东西)使用System.Linq.Queryable。检查这些方法的参数。
  • ScottGu解释。简而言之,Linq内存中将产生一些匿名方法来解决您的查询。Linq to SQL将产生一个表示查询的表达式树,然后将该树转换为T-SQL。Linq to Entities将产生一个表示查询的表达式树,然后将该树转换为适合平台的SQL。

3
表达式类型?在我看来,这听起来像是新领域。在哪里可以找到有关表达式类型和在C#中使用表达式树的更多信息?
MojoFilter

2
甚至更长的答案-也有奇怪的原因,为什么它们也可以转换为不同的代表类型:)
Jon Skeet

请注意,如果lambda是表达式lambda,则只能将其分配给Expression类型。
Micha Wiedenmann

125

我喜欢艾米(Amy)的回答,但我认为我会做书呆子。这个问题说,“一旦它编译” -这表明两个表达式被编译。它们都如何编译,但是其中一个被转换为委托,另一个被转换为表达式树?这是一个棘手的问题-您必须使用匿名方法的另一功能;唯一不被lambda表达式共享的表达式。如果您指定一个匿名方法而不指定任何参数列表则它与返回void且没有任何out参数的任何委托类型兼容。掌握了这些知识之后,我们应该能够构造两个重载,以使表达式完全明确而又非常不同。

但是灾难来袭!至少在C#3.0中,您不能将带有块主体的lambda表达式转换为表达式-也不能在主体中具有赋值的情况下转换lambda表达式(即使将其用作返回值)。这可能会随着C#4.0和.NET 4.0的变化而改变,从而允许在表达式树中表达更多内容。因此,换句话说,借助MojoFilter给出的示例,几乎总是将两者转换为同一件事。(稍后会详细介绍。)

如果稍微改变一下主体,我们可以使用委托参数技巧:

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

可是等等!如果我们足够狡猾的话,即使不使用表达式树,我们也可以区分两者。下面的示例使用重载解析规则(和匿名委托匹配技巧)...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

哎哟。记住孩子们,每当您重载从基类继承的方法时,一只小猫就会开始哭泣。


9
我拿出爆米花,读了整个东西。即使我直视它,这也是我可能永远不会想到的区别。
MojoFilter

27
我知道其中的一些内容,但我必须祝贺您能够将其传达给人类。
艾米B

1
如果您对.NET 4.0(基于CTP)中的更改感兴趣,请访问marcgravell.blogspot.com/2008/11/future-expressions.html。请注意,就我所知,C#4.0并没有做任何新的事情。
马克·格雷韦尔

4
乔恩,你很摇滚。Erik,要成为真正的Skeet粉丝,您应该像我一样订阅他的堆栈溢出RSS。只需将stackoverflow.com/users/22656插入您的供稿阅读器即可。
Paul Batum

3
@RoyiNamir:如果使用不带参数列表的匿名方法,则该方法与具有非ref / out参数的任何委托类型兼容,只要返回类型兼容即可。基本上,您说的是“我不在乎参数”。请注意,delegate { ... }一样的delegate() { ... }-后者只与一个无参数的委托类型兼容。
乔恩·斯基特

2

在上面的两个示例中,没有差异,为零。

表达方式:

() => { x = 0 }

是带有语句主体的Lambda表达式,因此无法将其编译为表达式树。实际上,它甚至不编译,因为它在0之后需要一个分号:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 

6
当然,这意味着存在一个差异“一个将进行编译,另一个将不会进行编译”;)
乔恩·斯凯特

2

艾米B是正确的。请注意,使用表达式树可能会有好处。LINQ to SQL将检查表达式树并将其转换为SQL。

您还可以使用lamda和表达式树来玩弄技巧,以有效的重构安全方式将类成员的名称有效地传递给框架。 最小起订量就是一个例子。


-1

它们是有区别的

例:

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(delegate
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});   

我用lambda代替:(错误)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});

错误〜lambda失败,仅因为方法参数签名不匹配。
杰克·王

-1

这里有一些基础知识。

这是一个匿名方法

(string testString) => { Console.WriteLine(testString); };

由于匿名方法没有名称,因此我们需要一个委托,在其中可以分配这两种方法或表达式。例如

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

与lambda表达式相同。通常我们需要一个委托来使用它们

s => s.Age > someValue && s.Age < someValue    // will return true/false

我们可以使用func委托来使用此表达式。

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.