C#Lambdaexpression式:为什么要使用它们?

我已经快速阅读了Microsoft Lambda Expression文档。

虽然这样的例子帮助我更好地理解:

delegate int del(int i); del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 

不过,我不明白为什么这是一个创新。 这只是一个方法,当“方法variables”结束时就会死掉,对吗? 为什么我应该用这个而不是真正的方法?

Lambdaexpression式对于匿名委托来说是一种更简单的语法,可以在任何地方使用匿名委托来使用。 然而事实恰恰相反, lambdaexpression式可以被转换为expression式树,这允许LINQ to SQL等许多魔术。

以下是LINQ to Objectsexpression式的一个例子,它使用匿名委托,然后使用lambdaexpression式来展示他们的眼睛有多容易:

 // anonymous delegate var evens = Enumerable .Range(1, 100) .Where(delegate(int x) { return (x % 2) == 0; }) .ToList(); // lambda expression var evens = Enumerable .Range(1, 100) .Where(x => (x % 2) == 0) .ToList(); 

Lambdaexpression式和匿名委托相对于编写单独的函数具有优势:它们实现了闭包 ,它可以允许您将本地状态传递给函数,而无需向函数添加参数或创build一次性使用的对象。

expression式树是C#3.0中一个非常强大的新function,它允许API查看expression式的结构,而不是仅仅获取对可执行方法的引用。 一个API只需要将一个委托参数放入一个Expression<T>参数中,编译器将从一个lambda生成一个expression式树,而不是一个匿名委托:

 void Example(Predicate<int> aDelegate); 

叫:

 Example(x => x > 5); 

变为:

 void Example(Expression<Predicate<int>> expressionTree); 

后者将通过描述expression式x > 5的抽象语法树的表示。 LINQ to SQL依靠这种行为能够将C#expression式转换为服务器端的过滤/sorting等所需的SQLexpression式。

匿名函数和expression式对于不能从创build完整方法所需的额外工作中受益的一次性方法是有用的。

考虑这个例子:

  string person = people.Find(person => person.Contains("Joe")); 

  public string FindPerson(string nameContains, List<string> persons) { foreach (string person in persons) if (person.Contains(nameContains)) return person; return null; } 

这些在function上是等同的。

我发现它们在我想使用另一个控件声明某个控件事件的处理程序的情况下很有用。 要做到这一点,通常你必须在类的字段中存储控件的引用,这样你就可以用不同于创build的方法来使用它们。

 private ComboBox combo; private Label label; public CreateControls() { combo = new ComboBox(); label = new Label(); //some initializing code combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged); } void combo_SelectedIndexChanged(object sender, EventArgs e) { label.Text = combo.SelectedValue; } 

感谢lambdaexpression式,你可以像这样使用它:

 public CreateControls() { ComboBox combo = new ComboBox(); Label label = new Label(); //some initializing code combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;}; } 

更容易。

例如,Lambda清理了C#2.0的匿名委托语法

 Strings.Find(s => s == "hello"); 

这是在C#2.0中完成的:

 Strings.Find(delegate(String s) { return s == "hello"; }); 

在function上,他们做的是完全一样的东西,它只是一个更简洁的语法。

这只是使用lambdaexpression式的一种方法。 您可以在任何可以使用委托的地方使用lambdaexpression式。 这可以让你做这样的事情:

 List<string> strings = new List<string>(); strings.Add("Good"); strings.Add("Morning") strings.Add("Starshine"); strings.Add("The"); strings.Add("Earth"); strings.Add("says"); strings.Add("hello"); strings.Find(s => s == "hello"); 

这段代码会在列表中search匹配单词“hello”的条目。 另一种方法是实际将代理传递给Find方法,如下所示:

 List<string> strings = new List<string>(); strings.Add("Good"); strings.Add("Morning") strings.Add("Starshine"); strings.Add("The"); strings.Add("Earth"); strings.Add("says"); strings.Add("hello"); private static bool FindHello(String s) { return s == "hello"; } strings.Find(FindHello); 

编辑

在C#2.0中,这可以使用匿名委托语法完成:

  strings.Find(delegate(String s) { return s == "hello"; }); 

Lambda大大清理了这个语法。

微软给了我们一个更简洁,更便捷的创build名为Lambdaexpression式的匿名委托的方法。 但是,对这个声明的expression部分没有太多的关注。 微软发布了一个完整的命名空间System.Linq.Expressions ,它包含了基于lambdaexpression式创buildexpression式树的类。 expression式树由表示逻辑的对象组成。 例如,x = y + z是一个expression式,可能是.Net中expression式树的一部分。 考虑以下(简单)示例:

 using System; using System.Linq; using System.Linq.Expressions; namespace ExpressionTreeThingy { class Program { static void Main(string[] args) { Expression<Func<int, int>> expr = (x) => x + 1; //this is not a delegate, but an object var del = expr.Compile(); //compiles the object to a CLR delegate, at runtime Console.WriteLine(del(5)); //we are just invoking a delegate at this point Console.ReadKey(); } } } 

这个例子是微不足道的。 我相信你在想,“这是没用的,因为我可以直接创build委托,而不是创buildexpression式并在运行时编译它”。 你会是对的。 但是这为expression树提供了基础。 Expressions命名空间中有许多可用的expression式,您可以构build自己的expression式。 我想你可以看到,这可能是有用的,当你不知道什么algorithm应该在devise或编译时间。 我看到一个例子,用它来写一个科学计算器。 您也可以将其用于贝叶斯系统或遗传编程 (AI)。 在我的职业生涯中,我不得不编写类似Excel的function,允许用户input简单的expression式(添加,删除等)来操作可用的数据。 在.net 3.5中,我不得不求助于C#之外的某种脚本语言,或者不得不使用reflection中的代码发射function来即时创build.Net代码。 现在我将使用expression式树。

它节省了必须在特定的地方只使用一次的方法远离他们使用的地方。 良好的用途是比较sorting等通用algorithm,然后在那里你可以定义一个自定义的sorting函数,而你正在调用sorting,而不是远离强迫你去别处看看你正在sorting。

这不是一个真正的创新。 LISP具有约30年或更长时间的lambdafunction。

您也可以在编写generics代码时使用lambdaexpression式来处理您的方法。

例如:通用函数来计算方法调用所花费的时间。 (即Action在这里)

 public static long Measure(Action action) { Stopwatch sw = new Stopwatch(); sw.Start(); action(); sw.Stop(); return sw.ElapsedMilliseconds; } 

你可以用下面的lambdaexpression式来调用上面的方法,

 var timeTaken = Measure(() => yourMethod(param)); 

expression式允许你从你的方法获得返回值,也可以从参数中获取返回值

 var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam)); 

很多时候,你只是在一个地方使用这个function,所以做一个方法就是把课堂搞得一团糟。

Lambdaexpression式是表示匿名方法的简明方式。 匿名方法和Lambdaexpression式都允许您定义内联的方法实现,但是,匿名方法明确要求您为方法定义参数types和返回types。 Lambdaexpression式使用C#3.0的types推断function,允许编译器根据上下文来推断variables的types。 这是非常方便,因为这节省了我们很多打字!

这是一种采取小操作的方式,并将其放在非常接近它的位置的位置(与声明接近其使用点的variables不同)。 这应该让你的代码更具可读性。 通过对expression式进行匿名处理,如果某个人在其他地方使用该function并修改为“增强”该function,那么对于某人破坏客户端代码而言,也会使其变得难上加难。

同样,为什么你需要使用foreach? 您可以使用简单的for循环执行foreach中的所有操作,也可以直接使用IEnumerable。 答:你不需要它,但它使你的代码更具可读性。

lambdaexpression式就像是代替委托实例写的匿名方法。

 delegate int MyDelagate (int i); MyDelagate delSquareFunction = x => x * x; 

考虑lambdaexpression式x => x * x;

input参数值是x(在=>的左侧)

函数逻辑是x * x(在=>的右边)

一个lambdaexpression式的代码可以是一个语句块而不是一个expression式。

 x => {return x * x;}; 

注意: Func是一个预定义的generics委托。

  Console.WriteLine(MyMethod(x => "Hi " + x)); public static string MyMethod(Func<string, string> strategy) { return strategy("Lijo").ToString(); } 

参考

  1. 代表和接口如何互换使用?

types安全和透明度的创新。 尽pipe你没有声明lambdaexpression式的types,但它们被推断出来了,可以被代码search,静态分析,重构工具和运行时reflection所使用。

例如,在您使用SQL之前,可能会收到SQL注入攻击,因为黑客通常会传递一个string,而数字通常是预期的。 现在,您将使用一个LINQ lambdaexpression式,该expression式受到保护。

在纯代理上构buildLINQ API是不可能的,因为它需要在评估它们之前将expression式树组合在一起。

在2016年,大多数stream行的语言都支持lambdaexpression式 ,而C#则是这种进化中的主stream命令式语言之一。

这也许是为什么使用lambdaexpression式的最好的解释 – > https://youtu.be/j9nj5dTo54Q

总而言之,这是为了提高代码的可读性,通过重复使用而不是复制代码来减less错误的机会,并利用幕后的优化。