委托关键字与 lambda 表示法

Posted

技术标签:

【中文标题】委托关键字与 lambda 表示法【英文标题】:delegate keyword vs. lambda notation 【发布时间】:2010-09-22 22:01:33 【问题描述】:

一经编译,有没有区别:

delegate  x = 0; 

() =>  x = 0 

?

【问题讨论】:

【参考方案1】:

简短回答:没有。

可能不相关的较长答案:

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

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

System.Linq.Expression.Expression(TDelegate)(从这里开始)。 带有委托(例如 System.Func)的 Linq in-memory 使用 System.Linq.Enumerable。带有表达式的 Linq to SQL(和其他任何东西)使用 System.Linq.Queryable。查看这些方法的参数。 Explanation from ScottGu。简而言之,Linq in-memory 会产生一些匿名方法来解决您的查询。 Linq to SQL 将生成一个表示查询的表达式树,然后将该树转换为 T-SQL。 Linq to Entities 将生成一个表示查询的表达式树,然后将该树转换为适合平台的 SQL。

【讨论】:

表达式类型?这对我来说听起来像是新的领域。在哪里可以找到有关表达式类型和在 C# 中使用表达式树的更多信息? 更长的答案——它们也可以转换为不同的委托类型有很多奇怪的原因:) 请注意,如果是表达式 lambda,则 lambda 只能分配给 Expression 类型。【参考方案2】:

我喜欢 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;  );
    

哎哟。请记住,孩子们,每次重载从基类继承的方法时,小猫都会哭泣。

【讨论】:

我拿出爆米花读了整本书。这是一些我可能永远不会想到的区别,即使我正对着它。 我知道一些,但我必须祝贺你有能力将它传达给人类。 对于任何对 .NET 4.0(基于 CTP)的更改感兴趣的人 - 请参阅 marcgravell.blogspot.com/2008/11/future-expressions.html。请注意,据我所知,C# 4.0 还没有做任何新的事情。 乔恩,你摇滚。 Erik,要成为一个真正的 Skeet fanboi,你应该像我一样订阅他的堆栈溢出 rss。只需将***.com/users/22656 粘贴到您的提要阅读器中即可。 @RoyiNamir:如果您使用没有参数列表的匿名方法,那么它与具有非 ref/out 参数的 any 委托类型兼容,只要返回类型为兼容的。基本上你是在说“我不在乎参数”。请注意,delegate ... delegate() ... 相同 - 后者仅与无参数委托类型兼容。【参考方案3】:

在上面的两个示例中,没有区别,为零。

表达式:

() =>  x = 0 

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

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

【讨论】:

当然这意味着“一个会编译,另一个不会”;)【参考方案4】:

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

您还可以使用 lamdas 和表达式树来以重构安全的方式有效地将类成员的名称传递给框架。 Moq 就是一个例子。

【讨论】:

【参考方案5】:

有区别

例子:

var mytask = Task.Factory.StartNew(() =>

    Thread.Sleep(5000);
    return 2712;
);
mytask.ContinueWith(delegate

    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
);   

我用 lambda:(error) 替换

var mytask = Task.Factory.StartNew(() =>

    Thread.Sleep(5000);
    return 2712;
);
mytask.ContinueWith(()=>

    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
);

【讨论】:

Wrong~lambda 失败只是因为方法参数签名不匹配。【参考方案6】:

这里有一些基础知识。

这是一个匿名方法

(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);

【讨论】:

以上是关于委托关键字与 lambda 表示法的主要内容,如果未能解决你的问题,请参考以下文章

Lambda(Linq)

Delegate(委托)

lambda,linq

part01.03 委托与 Lambda 表达式:委托

C# 委托 线程

Python:lambda表达式和yield关键字理解与使用讲解