C#Lambda表达式:为什么要使用它们?
我已经快速阅读了Microsoft Lambda Expression文档。
但是,这种示例有助于我更好地理解:
delegate int del(int i); del myDelegate = x => x * x; int j = myDelegate(5); //j = 25
不过,我仍然不明白为什么这是一项创新。这只是一种在"方法变量"结束时消失的方法,对吗?为什么要使用此方法而不是实际方法?
解决方案
很多时候,我们只在一个地方使用功能,因此制作方法只会使类混乱。
不必将方法定义在远离使用位置的地方,而不必在特定位置仅使用一次。很好的用途是用作排序等通用算法的比较器,然后我们可以在其中定义自定义排序函数,以在其中调用排序,而不是进一步强迫我们到其他地方查看正在排序的内容。
这并不是真正的创新。 LISP具有lambda函数已超过30年。
匿名函数和表达式对于一次性方法很有用,这些方法无法从创建完整方法所需的额外工作中受益。
考虑以下示例:
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; }
这些在功能上是等效的。
这是一种进行小操作并将其非常靠近其使用位置的方式(与声明变量接近其使用点的方式不同)。这应该使代码更具可读性。通过使表达式匿名化,如果该函数在其他地方使用并被修改为"增强"功能,我们将使他人更难以破坏客户端代码。
同样,为什么需要使用foreach?我们可以使用普通的for循环或者仅直接使用IEnumerable在foreach中进行所有操作。答:我们不需要它,但是它使代码更具可读性。
Lambda表达式是表示匿名方法的一种简洁方法。匿名方法和Lambda表达式都允许我们内联定义方法实现,但是,匿名方法明确要求我们定义方法的参数类型和返回类型。 Lambda表达式使用C3.0的类型推断功能,该功能允许编译器根据上下文推断变量的类型。它非常方便,因为这样可以节省很多打字时间!
这只是使用lambda表达式的一种方式。我们可以在可以使用委托的任何地方使用lambda表达式。这使我们可以执行以下操作:
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");
此代码将在列表中搜索与单词" 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);
编辑:
在C2.0中,可以使用匿名委托语法完成此操作:
strings.Find(delegate(String s) { return s == "hello"; });
Lambda大大清除了该语法。
Lambda表达式是匿名委托的一种较简单的语法,可以在可以使用匿名委托的任何地方使用。但是,事实并非如此。可以将lambda表达式转换为表达式树,这使LINQ to SQL有了很多魔力。
下面是一个使用匿名委托然后使用lambda表达式的LINQ to Objects表达式的示例,以显示它们在眼睛上有多容易:
// 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();
Lambda表达式和匿名委托比编写单独的函数有一个优势:它们实现闭包,可以使我们将局部状态传递给函数,而无需向函数添加参数或者创建一次性对象。
表达式树是C3.0的一项非常强大的新功能,它允许API查看表达式的结构,而不仅仅是获取对可以执行的方法的引用。 API只需将一个委托参数变成一个" Expression <T>"参数,编译器就会从一个lambda生成一个表达式树,而不是一个匿名委托:
void Example(Predicate<int> aDelegate);
像这样
Example(x => x > 5);
变成:
void Example(Expression<Predicate<int>> expressionTree);
后者将传递一个抽象语法树的表示形式,该抽象语法树描述了表达式" x> 5"。 LINQ to SQL依靠此行为,能够将Cexpressions转换为服务器端进行过滤/排序等所需的SQL表达式。
Lambda清理了C2.0的匿名委托语法...例如
Strings.Find(s => s == "hello");
在C2.0中是这样完成的:
Strings.Find(delegate(String s) { return s == "hello"; });
从功能上讲,它们执行完全相同的操作,只是语法更加简洁。
微软为我们提供了一种更清洁,更便捷的方式来创建名为Lambda表达式的匿名委托。但是,该语句的表达式部分没有引起太多关注。微软发布了整个命名空间System.Linq.Expressions,其中包含用于基于lambda表达式创建表达式树的类。表达式树由代表逻辑的对象组成。例如,x = y + z是一个表达式,它可能是.Net中表达式树的一部分。考虑以下(简单)示例:
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(); } } }
这个例子很简单。而且我敢肯定,我们在想:"这没什么用,因为我可以直接创建委托,而不是在运行时创建表达式并进行编译"。你会是对的。但这为表达式树提供了基础。 Expressions命名空间中有许多表达式,我们可以构建自己的表达式。我想我们会发现,当我们不确切知道算法在设计或者编译时应该使用什么时,这可能会很有用。我在某处看到了一个使用此示例编写科学计算器的示例。我们也可以将其用于贝叶斯系统或者遗传编程(AI)。在我的职业生涯中,有几次我不得不编写类似于Excel的功能,该功能允许用户输入简单的表达式(加法,减法等)以对可用数据进行操作。在.Net 3.5之前的版本中,我不得不求助于C#外部的某些脚本语言,或者不得不使用反射代码的功能来动态创建.Net代码。现在,我将使用表达式树。
当我想使用另一个控件为某个控件的事件声明处理程序时,我发现它们很有用。
为此,通常必须将控件的引用存储在类的字段中,以便可以将其用于与创建控件不同的方法中。
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; }
多亏了lambda表达式,我们可以像这样使用它:
public CreateControls() { ComboBox combo = new ComboBox(); Label label = new Label(); //some initializing code combo.SelectedIndexChanged += (s, e) => {label.Text = combo.SelectedValue;}; }
容易得多。