Lambda for Dummies …任何人,任何人? 我想不是

为了理解这个非常奇怪的'=>'操作符,我find了一个很好的开始 ,作者非常简洁明了:

parameters => expression 

有没有人有任何提示,理解lambda的基础知识,以便更容易'破译'更复杂的lambda语句?

举例来说:如果给我一些像(从我在这里收到的答复 ):

 filenames.SelectMany(f => Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) .Cast<PluginClassAttribute>() .Select(a => a.PluginType) ).ToList(); 

我怎么能把这个分解成更简单的部分呢?


更新:想炫耀我的第一个lambdaexpression式。 不要嘲笑我,但是我没有模仿别人的榜样,而是第一次运作:

 public ModuleData[] GetStartModules( ) { return modules.FindAll(start => start.IsBatch == true).ToArray(); } 

我们来剖析你的代码示例:

 filenames.SelectMany(f => Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) .Cast<PluginClassAttribute>() .Select(a => a.PluginType) ).ToList(); 

所以,我们从一个名为filenamesstring[]开始。 我们调用数组的SelectMany扩展方法,然后调用ToList的结果:

 filenames.SelectMany( ... ).ToList(); 

SelectMany以委托为参数,在这种情况下,委托必须将typesstring一个参数作为input,并返回一个IEnumerable<T> (其中T的types被推断)。 这是lambda进入阶段的地方:

 filenames.SelectMany(f => Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) ).ToList() 

这里将会发生的是, 对于 filenames数组中的每个元素,委托将被调用。 f是input参数,并且=>的右边是代表所指的方法体。 在这种情况下, Assembly.LoadFrom将在数组中的文件名中被调用,并使用f参数将文件名传递给LoadFrom方法。 在返回的AssemblyInstance ,将调用GetCustomAttributes(typeof(PluginClassAttribute), true) ,它将返回一个Attribute实例数组。 所以编译器不能推断出前面提到的T的types是Assembly

在返回的IEnumerable<Attribute>上, Cast<PluginClassAttribute>()将被调用,返回一个IEnumerable<PluginClassAttribute>

所以现在我们有一个IEnumerable<PluginClassAttribute> ,我们调用SelectSelect方法类似于SelectMany ,但返回typesT (由编译器推断)的单个实例,而不是IEnumerable<T> 。 设置是相同的; 对于IEnumerable<PluginClassAttribute>中的每个元素,它将调用已定义的委托,将当前元素值传递给它:

 .Select(a => a.PluginType) 

同样, a是input参数, a.PluginType是方法体。 因此,对于列表中的每个PluginClassAttribute实例,它将返回PluginType属性的值(我将假定此属性的types为Type )。

执行摘要
如果我们把这些零碎的东西粘在一起:

 // process all strings in the filenames array filenames.SelectMany(f => // get all Attributes of the type PluginClassAttribute from the assembly // with the given file name Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true) // cast the returned instances to PluginClassAttribute .Cast<PluginClassAttribute>() // return the PluginType property from each PluginClassAttribute instance .Select(a => a.PluginType) ).ToList(); 

兰巴达与代表
让我们通过比较lambdaexpression式来完成这个过程。 采取以下列表:

 List<string> strings = new List<string> { "one", "two", "three" }; 

假设我们想要过滤那些以字母“t”开头的词:

 var result = strings.Where(s => s.StartsWith("t")); 

这是最常见的方法; 使用lambdaexpression式来设置它。 但是还有其他的select:

 Func<string,bool> func = delegate(string s) { return s.StartsWith("t");}; result = strings.Where(func); 

这基本上是一样的:首先我们创build一个types为Func<string, bool> (这意味着它将一个string作为input参数,并返回一个bool )。 然后我们将该委托作为parameter passing给Where方法。 这是编译器在第一个示例( strings.Where(s => s.StartsWith("t")); )后面为我们做的。

第三个select是简单地将委托传递给非匿名方法:

 private bool StringsStartingWithT(string s) { return s.StartsWith("t"); } // somewhere else in the code: result = strings.Where(StringsStartingWithT); 

所以,在我们看这里的情况下,lambdaexpression式是定义委托的一种相当紧凑的方式,通常指的是匿名方法。

如果你有能量在这里读,那么,谢谢你的时间:)

因此,从可怕的定义开始 – lambda是定义匿名方法的另一种方式。 有(从C#2.0我相信)是一种方法来构build匿名方法 – 但是,语法是非常…不方便。

那么什么是匿名方法? 这是一种内联方法,没有名字 – 因此是匿名的。 如果您有一个接受委托的方法,那么这很有用,因为您可以将此lambdaexpression式/匿名方法作为parameter passing,因为types匹配。 以IEnumerable.Select为例,它定义如下:

 IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector); 

如果你想在一个List上使用这个方法,并select每个元素两次(即连接到它自己):

 string MyConcat(string str){ return str + str; } ... void myMethod(){ IEnumerable<string> result = someIEnumerable.Select(MyConcat); } 

这是非常不方便的做法,特别是如果你想执行许多这些操作 – 通常这些types的方法只能使用一次。 你显示的定义(参数=>expression)非常consise并击中它。 关于lambda的一个有趣的事情是,你不需要expression参数的types – 只要它们可以从expression式中传递出来 。 IE浏览器。 在Select的情况下 – 我们知道第一个参数必须是TSourcetypes – 因为方法的定义如此。 更进一步,如果我们像foo.Select(…)那样调用方法,那么expression式的返回值将定义TResult。

一个有趣的事情是,对于单语句lambda的return关键字是不需要的 – lambda将返回任何一个expression式的计算结果。 但是,如果您使用块(包裹在“{”和“}”中),那么您必须像往常一样包含return关键字。

如果愿意的话,定义参数的types仍然是100%合法的。 有了这些新的知识,我们试着重写前面的例子:

 void myMethod(){ IEnumerable<string> result = someIEnumerable.Select(s => s + s); } 

或者,有明确的参数

 void myMethod(){ IEnumerable<string> result = someIEnumerable.Select((string s) => s + s); } 

lambda在C#中另一个有趣的特性是它们用来构造expression式树 。 这可能不是“初学者”的材料,但简而言之,expression式树包含所有关于lambda的元数据,而不是可执行代码。

那么,你可以看到一个lambda作为一个快速的方法来写一个你只想使用一次的方法。 比如说下面的方法

 private int sum5(int n) { return n + 5; } 

相当于λ: (n) => n + 5 。 除了你可以在你的类的任何地方调用方法,并且lambda只在声明的范围内生存(也可以将lambda存储在Action和Func对象中)

lambdas可以为你做的其他事情是捕捉范围,build立封闭。 例如,如果你有这样的事情:

 ...methodbody int acc = 5; Func<int> addAcc = (n) => n + acc; 

你在那里有一个函数接受一个参数,但添加的数量取自variables的值。 即使在acc定义的范围完成之后,lambda也可以存活。

你可以用lambda来构build非常整洁的东西,但是你必须小心,因为有时候你会用这样的技巧来降低可读性。

正如其他人所说,lambdaexpression式是函数的符号。 它将expression式右侧的自由variables绑定到左侧的参数。

 a => a + 1 

创build一个将expression式(a + 1)中的自由variablesa绑定到函数的第一个参数并返回该函数的函数。

Lambdas非常有用的一种情况是当你使用它们来处理列表结构时。 System.Linq.Enumerable类提供了许多有用的函数,使您可以使用实现IEnumerable的Lambdaexpression式和对象。 例如Enumerable.Where可以用来过滤一个列表:

 List<string> fruits = new List<string> { "apple", "passionfruit", "banana", "mango", "orange", "blueberry", "grape", "strawberry" }; IEnumerable<string> shortFruits = fruits.Where(fruit => fruit.Length < 6); foreach (string fruit in shortFruits) { Console.WriteLine(fruit); } 

产量将是“苹果,芒果,葡萄”。

尝试了解这里发生了什么:expression式fruit => fruit.Length <6创build一个函数,如果参数的Length属性小于6,则返回true

Enumerable.Where遍历List并创build一个新的List,其中只包含提供的函数返回true的那些元素。 这样可以节省编写迭代List的代码,检查每个元素的谓词并做一些事情。

针对熟悉编码而不熟悉lambda的开发人员的一个简单的解释就是TekPub上的这个简单的video

TekPub – 概念:#2 Lambdas

你显然在这里有很多的反馈,但这是另一个很好的来源和简单的解释。

我知道这是有点老,但我来到这里试图理解这一切lambda的东西。 当我完成所有的答案和评论时,我对lambda有了更好的理解,并认为我应该添加这个简单的答案(从学习者的angular度来看,学习者):

不要将a => a + 1混淆,意思是将1加1并将结果返回给a。 (这很可能是初学者混淆的原因,而是看起来像这样:a是函数的input参数(未命名函数),+ 1是函数中的语句(未命名函数构造的飞')。

希望这可以帮助 :)

Lambda演算在许多编程语言中都很常见。 他们在一些语言中也被称为匿名函数。 虽然不同的语言对lambda有不同的语法,但是原理是一样的,它们的各个部分通常是相同的。

也许最有名的是Javascript的匿名函数。

 lol = function() {laugh()} # is equivalent to function lol() {laugh()} 

有什么不同? 那么,有时你不想经历创build一个函数的麻烦,只是把它传递到某个地方,然后再也不能。

 window.onload = function() {laugh()} # is way easier than function lol() {laugh()} window.onload = lol 

您可以查看维基百科的文章以获取相关信息,或者您可以在同一篇文章中直接跳至Lambda编程 。

这只是C#写下函数值的符号。 它不需要给函数一个名字,因此这个值有时被称为匿名函数 。 其他语言有其他符号,但它们总是包含一个参数列表和一个主体。

由Alonzo Church在1930年代为他的Lambda微积分而发明的原始符号使用expression式λx.t的希腊字符lambda来表示一个函数,即名称。

我的理解lambda的基本知识的提示是双重的。

首先我build议学习函数式编程。 在这方面,Haskell是一个很好的语言。 我正在使用的这本书,从Graham Hutton 编写的Haskell编程中获得了很多。 这为Haskell提供了良好的基础,并且包含lambda的解释。

从那里我想你应该看看Erik Meijer的函数式编程讲座,因为他们给函数式编程介绍了一个很好的介绍,也使用了Haskell,并且跨越了C#。

一旦你已经采取了一切,你应该很好地了解lambdas。

CodeProject最近有一个不错的介绍性文章: C#委托,匿名方法和Lambdaexpression式 – O我的!