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();
所以,我们从一个名为filenames
的string[]
开始。 我们调用数组的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>
,我们调用Select
。 Select
方法类似于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我的!