[原译]Lambda高手之路第三部分

简介: 原文:[原译]Lambda高手之路第三部分 背后的秘密-MSIL 通过著名的LINQPad,我们可以更深入的查看MSIL代码而没有任何秘密。下图是一个LINQPad的使用截图 我们会看三个例子,第一个Lambda表达式如下: Action DoSomethingLambda = (s) => { Console.

原文:[原译]Lambda高手之路第三部分

背后的秘密-MSIL

通过著名的LINQPad,我们可以更深入的查看MSIL代码而没有任何秘密。下图是一个LINQPad的使用截图

我们会看三个例子,第一个Lambda表达式如下:

Action<string> DoSomethingLambda = (s) =>
{
    Console.WriteLine(s);// + local
};

 

对应的普通函数是这样的

Action<string> DoSomethingLambda = (s) =>
{
    Console.WriteLine(s);// + local
};

 

生成的MSIL代码片段如下:

DoSomethingNormal:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  call        System.Console.WriteLine
IL_0007:  nop         
IL_0008:  ret         
<Main>b__0:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  call        System.Console.WriteLine
IL_0007:  nop         
IL_0008:  ret       

 

最大的不同是方法的名称用法不同。而不是声明。事实上。声明是完全一样的。编译器在类里面创建了一个新的方法来实现这个方法。这没什么新东西,仅仅是为了我们使用Lambda表达式方便代码编写。从MSIL代码中,我们做了同样的事情。在当前对象里调用了一个方法。

我们在下图里演示一下编译器所做的修改。在这个图例。我们可以看到编译器把Lambda表达式移动成了一个固定的方法。


第二个例子将展示Lambda表达式真正的奇妙之处,在这个例子里。我们既使用了有着全局变量的普通方法也使用了有捕获变量的Lambda表达式。代码如下

void Main()
{
    int local = 5;

    Action<string> DoSomethingLambda = (s) => {
        Console.WriteLine(s + local);
    };
    
    global = local;
    
    DoSomethingLambda("Test 1");
    DoSomethingNormal("Test 2");
}

int global;

void DoSomethingNormal(string s)
{
    Console.WriteLine(s + global);
}

 

没什么不同的似乎。关键是:lambda表达式如何被编译器处理

IL_0000:  newobj      UserQuery+<>c__DisplayClass1..ctor
IL_0005:  stloc.1     
IL_0006:  nop         
IL_0007:  ldloc.1     
IL_0008:  ldc.i4.5    
IL_0009:  stfld       UserQuery+<>c__DisplayClass1.local
IL_000E:  ldloc.1     
IL_000F:  ldftn       UserQuery+<>c__DisplayClass1.<Main>b__0
IL_0015:  newobj      System.Action<System.String>..ctor
IL_001A:  stloc.0     
IL_001B:  ldarg.0     
IL_001C:  ldloc.1     
IL_001D:  ldfld       UserQuery+<>c__DisplayClass1.local
IL_0022:  stfld       UserQuery.global
IL_0027:  ldloc.0     
IL_0028:  ldstr       "Test 1"
IL_002D:  callvirt    System.Action<System.String>.Invoke
IL_0032:  nop         
IL_0033:  ldarg.0     
IL_0034:  ldstr       "Test 2"
IL_0039:  call        UserQuery.DoSomethingNormal
IL_003E:  nop         

DoSomethingNormal:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  ldarg.0     
IL_0003:  ldfld       UserQuery.global
IL_0008:  box         System.Int32
IL_000D:  call        System.String.Concat
IL_0012:  call        System.Console.WriteLine
IL_0017:  nop         
IL_0018:  ret         

<>c__DisplayClass1.<Main>b__0:
IL_0000:  nop         
IL_0001:  ldarg.1     
IL_0002:  ldarg.0     
IL_0003:  ldfld       UserQuery+<>c__DisplayClass1.local
IL_0008:  box         System.Int32
IL_000D:  call        System.String.Concat
IL_0012:  call        System.Console.WriteLine
IL_0017:  nop         
IL_0018:  ret         

<>c__DisplayClass1..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  ret      

 

和第一个例子一样。机制相同。编译器把lambda表达式移动到一个方法里。但是不同的是,编译器这次还生成了一个类。编译器为我们的lambda表达式生成的方法会放在类里,这就给了捕获的变量一个全局的作用域,通过这样。Lambda表达式可以访问局部变量。因为在MSIL里。它是类实例里面的一个全局变量。

所有的变量因此就可以在新生成的类的对象里赋值/读取了。这解决了变量之间的引用问题。(其实就是只保留了对该类实例的引用。)编译器也足够智能之会把这些捕获的变量放到类里面。因此,当我们使用Lambda的时候才没有太大的性能问题。无论如何。注意。由于保持了对lambda表达式的引用,因此可能造成内存泄漏。只要方法还在。变量就仍然存活。显而易见。而现在我们知道了原因。

我们再次用图示来说明。这这种闭包情况下里。不仅仅方法会被移动。捕获的变量也会被移动。所有被移动了的对象会被放到一个新生成的类里。因此一个没有名称的类就隐式的出现了。

下一节将是映射流行的JavaScrpit模式。

目录
相关文章
|
8月前
|
Java 编译器 API
4.3 Lambda表达式的性能与限制:在某些情况下避免使用Lambda表达
4.3 Lambda表达式的性能与限制:在某些情况下避免使用Lambda表达
568 0
|
9月前
|
Java 开发者
lambda让代码更优雅
Lambda表达式是Java 8中引入的一个重要特性,它允许开发者以更简洁的方式编写匿名函数,使得代码更加紧凑和易读。Lambda表达式是函数式编程的一种体现,可以将函数作为方法的参数传递,并且可以使用更简洁的语法实现函数式接口(只有一个抽象方法的接口)的实例化。Lambda表达式的语法形式为 (参数列表) -> {表达式或语句块}。
53 0
|
11月前
|
算法 编译器 C++
C++ :Lambda函数的浅学习
C++ :Lambda函数的浅学习
152 0
|
Java API
一篇文章掌握lambda,function下41个类
Java8 发布以来,lambda 表达式简化了代码,增强了阅读性,代码更加简洁。lambda 主要是给Java增加了函数式编程的方式。lambda表达式的实现就是functionInterface。
83 0
一篇文章掌握lambda,function下41个类
笔记23-Lambda&方法引用
笔记23-Lambda&方法引用
|
JavaScript 前端开发 Java
别翻了,Lambda 表达式入门,看这篇就够了(1)
别翻了,Lambda 表达式入门,看这篇就够了
129 0
别翻了,Lambda 表达式入门,看这篇就够了(1)
|
Java
别翻了,Lambda 表达式入门,看这篇就够了(2)
别翻了,Lambda 表达式入门,看这篇就够了
86 0
|
编译器 C++
C++11 Lambda表达汇总总结
C++11 Lambda表达汇总总结
92 0
|
设计模式 JavaScript 前端开发
深入探讨 Lambda 表达式(下)
说明: > 由于 Lambda 表达式涉及的周边知识点实在太多,因此拆分为上、下两篇文章讲解。 > > 本篇为下篇,上篇请点击:[深入探讨 Lambda 表达式(上)](https://www.atatech.org/articles/159525) 目录介绍: ![1.png](https://ata2-img.cn-hangzhou.oss-pub.aliyun-inc.co
761 0
|
设计模式 SQL 存储
深入探讨 Lambda 表达式(上)
说明: 由于 Lambda 表达式涉及的周边知识点实在太多,因此拆分为上、下两篇文章讲解,本篇为上篇,下篇随后放出。 目录介绍: 上篇,主要讲 1~4 章节,下篇,主要介绍 5~8 章节。 序言 JDK8 日渐成为项目开发中的主流。 但平时的开发中,可能很多小伙伴都还没有适应 JDK8 的某些语法,还在沿用之前版本的习惯写业务代码,若是习惯 JDK8 的新特性,可能会让
1603 0