C# Lambda expressions: Why should I use them?
我已经快速阅读了Microsoft lambda表达式文档。
这种例子帮助我更好地理解了:
1 2 3 | delegate int del(int i); del myDelegate = x => x * x; int j = myDelegate(5); //j = 25 |
不过,我不明白为什么这是一个创新。它只是一个在"方法变量"结束时死亡的方法,对吗?为什么我要用这个来代替真正的方法?
lambda表达式是匿名委托的简单语法,可以在任何可以使用匿名委托的地方使用。然而,相反的情况并非如此;lambda表达式可以转换为表达式树,这允许像linq-to-sql这样的魔力。
下面是一个使用匿名委托然后使用lambda表达式的linq-to-objects表达式的示例,以显示它们在视觉上有多容易:
1 2 3 4 5 6 7 8 9 10 11 | // 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表达式和匿名委托比编写单独的函数有一个优势:它们实现闭包,允许您将本地状态传递给函数,而无需向函数添加参数或创建一次性使用的对象。
表达式树是C 3.0的一个非常强大的新特性,它允许API查看表达式的结构,而不只是获取可以执行的方法的引用。API只需将委托参数转换为
1 | void Example(Predicate<int> aDelegate); |
叫作:
1 | Example(x => x > 5); |
变成:
1 | void Example(Expression<Predicate<int>> expressionTree); |
后者将传递描述表达式
匿名函数和表达式对于一次性方法很有用,因为这些方法不能从创建完整方法所需的额外工作中获益。
考虑这个例子:
1 | string person = people.Find(person => person.Contains("Joe")); |
对战
1 2 3 4 5 6 7 | public string FindPerson(string nameContains, List<string> persons) { foreach (string person in persons) if (person.Contains(nameContains)) return person; return null; } |
它们在功能上是等效的。
当我想使用另一个控件为某个控件的事件声明一个处理程序时,我发现它们非常有用。要正常地执行此操作,您必须将控件的引用存储在类的字段中,这样您就可以在创建控件的不同方法中使用它们。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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表达式,您可以这样使用它:
2容易得多。
lambda清理了c 2.0的匿名委托语法…例如
1 | Strings.Find(s => s =="hello"); |
在C 2.0中完成,如下所示:
1 | Strings.Find(delegate(String s) { return s =="hello"; }); |
在功能上,它们做的是完全相同的事情,这只是一个更简洁的语法。
这只是使用lambda表达式的一种方法。可以在任何可以使用委托的地方使用lambda表达式。这允许您执行以下操作:
2此代码将在列表中搜索与单词"hello"匹配的条目。另一种方法是将委托实际传递给find方法,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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中,可以使用匿名委托语法:
1 | strings.Find(delegate(String s) { return s =="hello"; }); |
lambda明显清除了这种语法。
Microsoft为我们提供了一种更干净、更方便的方法来创建名为lambda表达式的匿名委托。然而,对于这个语句的表达式部分并没有太多的关注。Microsoft发布了整个命名空间System.Linq.Expressions,其中包含基于lambda表达式创建表达式树的类。表达式树由表示逻辑的对象组成。例如,x=y+z是一个表达式,它可能是.NET中表达式树的一部分。请考虑以下(简单)示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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(); } } } |
这个例子很简单。我确信您在想,"这是无用的,因为我可以直接创建委托,而不是创建表达式并在运行时编译它"。你是对的。但这为表达树提供了基础。表达式命名空间中有许多可用的表达式,您可以构建自己的表达式。我认为您可以看到,当您不知道算法在设计或编译时应该是什么时,这可能很有用。我在某个地方看到了一个用这个编写科学计算器的例子。您也可以将其用于贝叶斯系统,或者用于遗传编程(ai)。在我的职业生涯中,有几次我不得不编写类似Excel的功能,允许用户输入简单的表达式(加法、分式运算等)来操作可用的数据。在.NET 3.5之前的版本中,我不得不使用一些C语言之外的脚本语言,或者不得不使用反射中的代码发出功能来动态创建.NET代码。现在我将使用表达式树。
它省去了在一个特定的地方只使用一次的方法,而不需要在远离使用它们的地方定义它们。好的用途是作为诸如排序之类的通用算法的比较器,然后您可以定义一个自定义排序函数,在该函数中调用排序,而不是进一步强制您查找其他位置以查看排序所依据的内容。
这并不是真正的创新。Lisp有lambda函数大约30年或更长时间了。
您还可以在编写通用代码以对方法进行操作时找到lambda表达式的用法。
例如:用于计算方法调用所用时间的泛型函数。(即这里的
1 2 3 4 5 6 7 8 | public static long Measure(Action action) { Stopwatch sw = new Stopwatch(); sw.Start(); action(); sw.Stop(); return sw.ElapsedMilliseconds; } |
您可以使用lambda表达式调用上面的方法,如下所示,
1 | var timeTaken = Measure(() => yourMethod(param)); |
表达式允许您从方法和out参数中获取返回值
1 | var timeTaken = Measure(() => returnValue = yourMethod(param, out outParam)); |
lambda表达式类似于用匿名方法代替委托实例。
1 2 | delegate int MyDelagate (int i); MyDelagate delSquareFunction = x => x * x; |
考虑lambda表达式
lambda表达式的代码可以是语句块而不是表达式。
1 | x => {return x * x;}; |
例子
注:
1 2 3 4 5 6 | Console.WriteLine(MyMethod(x =>"Hi" + x)); public static string MyMethod(Func<string, string> strategy) { return strategy("Lijo").ToString(); } |
工具书类
lambda表达式是表示匿名方法的简洁方法。匿名方法和lambda表达式都允许您以内联方式定义方法实现,但是匿名方法显式要求您定义方法的参数类型和返回类型。lambda表达式使用C 3.0的类型推断功能,该功能允许编译器根据上下文推断变量的类型。这是非常方便的,因为这节省了我们很多打字时间!
很多时候,你只在一个地方使用功能,所以让一个方法把类搞得一团糟。
这是一种采用小操作并将其非常接近使用位置的方法(与在其使用点附近声明变量不同)。这是为了使代码更可读。通过匿名化表达式,如果将函数用于其他地方并修改为"增强"它,那么就很难有人破坏您的客户机代码。
同样,为什么需要使用foreach?您可以使用简单的for循环或直接使用IEnumerable在foreach中执行所有操作。答:您不需要它,但它使您的代码更具可读性。
这也许是解释为什么要使用lambda表达式的最好方法->https://youtu.be/j9nj5dto54q
总之,它的目的是提高代码的可读性,通过重用而不是复制代码来减少出错的可能性,并利用后台发生的优化。
创新在于类型安全和透明。虽然您没有声明lambda表达式的类型,但它们是推断出来的,可以由代码搜索、静态分析、重构工具和运行时反射使用。
例如,在您可能使用SQL并可能受到SQL注入攻击之前,因为黑客传递了一个字符串,通常需要一个数字。现在,您将使用一个Linq lambda表达式,它是受保护的。
在纯委托上构建LinqAPI是不可能的,因为它需要在评估表达式树之前将它们组合在一起。
2016年,大多数流行语言都支持lambda表达式,而c是主流命令式语言中这种进化的先驱之一。