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

IAb*_*act 36 lambda c#-3.0

在我寻求理解非常奇怪的'=>'运算符的过程中,我找到了一个好的起点,作者非常简洁明了:

parameters => expression
Run Code Online (Sandbox Code Playgroud)

有没有人有任何关于理解lambda基础知识的技巧,以便更容易"破译"更复杂的lambda语句?

例如:如果我得到类似的东西(根据我在这里收到答案):

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

我怎样才能将其分解为更简单的部分?


更新:想要展示我的第一个lambda表达式.不要嘲笑我,但是我没有复制别人的例子就这样做了......它第一次工作:

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

Fre*_*örk 40

让我们剖析您的代码示例:

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

所以,我们从一个string[]叫做开始filenames.我们SelectMany在数组上调用扩展方法,然后调用ToList结果:

filenames.SelectMany(
   ...
).ToList();
Run Code Online (Sandbox Code Playgroud)

SelectMany将委托作为参数,在这种情况下,委托必须将该类型的一个参数string作为输入,并返回IEnumerable<T>(其中T推断的类型).这是lambdas进入舞台的地方:

filenames.SelectMany(f => 
        Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
).ToList()
Run Code Online (Sandbox Code Playgroud)

这里会发生的是,对于filenames数组中的每个元素,将调用委托.f是输入参数,并且右边的任何内容是=>委托引用的方法体.在这种情况下,Assembly.LoadFrom将调用数组中的filename,LoadFrom使用f参数将文件名传递给方法.在AssemblyInstance返回的那个上,GetCustomAttributes(typeof(PluginClassAttribute), true)将被调用,返回一个Attribute实例数组.所以编译器无法推断出T前面提到的类型Assembly.

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

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

.Select(a => a.PluginType)
Run Code Online (Sandbox Code Playgroud)

同样,a输入参数a.PluginType是方法体.因此,对于PluginClassAttribute列表中的每个实例,它将返回PluginType属性的值(我将假设此属性属于该类型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();
Run Code Online (Sandbox Code Playgroud)

Lambdas与代表
让我们通过比较lambda到代表来完成这个.请按以下列表:

List<string> strings = new List<string> { "one", "two", "three" };
Run Code Online (Sandbox Code Playgroud)

假设我们要过滤掉以字母"t"开头的那些:

var result = strings.Where(s => s.StartsWith("t"));
Run Code Online (Sandbox Code Playgroud)

这是最常见的方法; 使用lambda表达式设置它.但也有其他选择:

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

这基本上是一回事:首先我们创建一个类型的委托Func<string, bool>(这意味着它接受一个string输入参数,并返回一个bool).然后我们将该委托作为参数传递给Where方法.这就是编译器在第一个示例(strings.Where(s => s.StartsWith("t"));)中的幕后操作.

三分之一的选择是简单地将委托传递给非匿名方法:

private bool StringsStartingWithT(string s)
{
    return s.StartsWith("t");
}

// somewhere else in the code:
result = strings.Where(StringsStartingWithT);
Run Code Online (Sandbox Code Playgroud)

因此,在我们这里看的情况下,lambda表达式是一种定义委托的相当紧凑的方式,通常是指匿名方法.

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


kas*_*ter 6

因此,从可怕的定义开始 - lambda是另一种定义匿名方法的方法.有(因为C#2.0我相信)是一种构建匿名方法的方法 - 但是这种语法非常......不方便.

那么什么是匿名方法?这是一种定义内联方法的方法,没有名称 - 因此是匿名的.如果您有一个接受委托的方法,这很有用,因为您可以将此lambda表达式/匿名方法作为参数传递,前提是类型匹配.以IEnumerable.Select为例,它定义如下:

IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector);
Run Code Online (Sandbox Code Playgroud)

如果您通常使用此方法说List并选择每个元素两次(连接到自身):

string MyConcat(string str){
    return str + str;
}

...

void myMethod(){
    IEnumerable<string> result = someIEnumerable.Select(MyConcat);
}
Run Code Online (Sandbox Code Playgroud)

这是一种非常不方便的方法,特别是如果您希望执行许多这些操作 - 通常也只使用一次这些方法.你展示的定义(参数=>表达式)是非常明确的,并且点击它.关于lambda的有趣之处在于你不需要表达参数的类型 - 只要它们可以从表达式中获取.IE浏览器.在Select的情况下 - 我们知道第一个参数必须是TSource类型 - 因为方法的定义表明了这一点.更进一步 - 如果我们将方法调用为foo.Select(...)则表达式的返回值将定义TResult.

一个有趣的事情是,对于单语句lambda,不需要return关键字 - lambda将返回一个表达式求值的任何内容.但是,如果您使用块(包含在"{"和"}"中),则必须像往常一样包含return关键字.

如果有人愿意,定义参数的类型仍然是100%合法的.有了这些新知识,让我们尝试重写前面的例子:

void myMethod(){
    IEnumerable<string> result = someIEnumerable.Select(s => s + s);
}
Run Code Online (Sandbox Code Playgroud)

或者,使用明确的参数说明

void myMethod(){
    IEnumerable<string> result = someIEnumerable.Select((string s) => s + s);
}
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*o F 5

好吧,你可以看到一个lambda作为编写一个你只想使用一次的方法的快捷方法.例如,以下方法

private int sum5(int n)
{
    return n + 5;
}
Run Code Online (Sandbox Code Playgroud)

相当于lambda : (n) => n + 5. 除了您可以在类中的任何位置调用该方法,并且lambda仅存在于声明的范围内(您还可以在Action和Func对象中存储lambda)

lambdas可以为你做的其他事情是捕获范围,构建一个闭包.例如,如果你有这样的东西:

...methodbody
int acc = 5;
Func<int> addAcc = (n) => n + acc;
Run Code Online (Sandbox Code Playgroud)

你所拥有的是一个像以前一样接受参数的函数,但是添加的数量来自变量的值.即使在定义acc的范围完成之后,lambda也可以存活.

你可以使用lambdas构建非常简洁的东西,但是你必须要小心,因为有时你会用这样的技巧失去可读性.