委托关键字与 lambda 表示法
Posted
技术标签:
【中文标题】委托关键字与 lambda 表示法【英文标题】:delegate keyword vs. lambda notation 【发布时间】:2010-09-22 22:01:33 【问题描述】:一经编译,有没有区别:
delegate x = 0;
和
() => x = 0
?
【问题讨论】:
【参考方案1】:简短回答:没有。
可能不相关的较长答案:
如果将 lambda 分配给委托类型(例如Func
或 Action
),您将获得匿名委托。
如果将 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 表示法的主要内容,如果未能解决你的问题,请参考以下文章