CodeSnippet.Cn
代码片段
Csharp
架构设计
.NetCore
西班牙语
kubernetes
MySql
Redis
Algorithm
Other
Ubuntu
Linux
.NetMvc
VisualStudio
Python
Git
pm
WPF
java
Plug-In
分布式
CSS
微服务架构
JavaScript
DataStructure
Shared
理解C#中的闭包
0
Csharp
小笨蛋
发布于:2022年04月26日
更新于:2022年04月26日
197
#custom-toc-container
### 1. 闭包的含义 首先闭包并不是针对某一特定语言的概念,而是一个通用的概念。除了在各个支持函数式编程的语言中,我们会接触到它。一些不支持函数式编程的语言中也能支持闭包(如java8之前的匿名内部类)。 在看过的对于闭包的定义中,个人觉得比较清晰的是在《JavaScript高级程序设计》这本书中看到的。具体定义如下: 闭包是指有权访问另一个函数作用域中的变量的函数。 注意,闭包这个词本身指的是一种函数。而创建这种特殊函数的一种常见方式是在一个函数中创建另一个函数。 ### 2.在C# 中使用闭包(例子选取自《C#函数式程序设计》) 下面我们通过一个简单的例子来理解C#闭包 ```csharp class Program { static void Main(string[] args) { Console.WriteLine(GetClosureFunction()(30)); } static Func
GetClosureFunction() { int val = 10; Func
internalAdd = x => x + val; Console.WriteLine(internalAdd(10)); val = 30; Console.WriteLine(internalAdd(10)); return internalAdd; } } ``` 上述代码的执行流程是Main函数调用`GetClosureFunction`函数,`GetClosureFunction`返回了委托`internalAdd`并被立即执行了。 输出结果依次为20、40、60 对应到一开始提出的闭包的概念。这个委托`internalAdd`就是一个闭包,引用了外部函数`GetClosureFunction`作用域中的变量val。 注意:`internalAdd`有没有被当做返回值和闭包的定义无关。就算它没有被返回到外部,它依旧是个闭包。 ### 3.理解闭包的实现原理 我们来分析一下这段代码的执行过程。在一开始,函数`GetClosureFunction`内定义了一个局部变量val和一个利用`lamdba`语法糖创建的委托`internalAdd`。 第一次执行委托`internalAdd` 10 + 10 输出20 接着改变了被`internalAdd`引用的局部变量值val,再次以相同的参数执行委托,输出40。显然局部变量的改变影响到了委托的执行结果。 `GetClosureFunction`将`internalAdd`返回至外部,以30作为参数,去执行得到的结果是60,和val局部变量最后的值30是一致的。 val 作为一个局部变量。它的生命周期本应该在`GetClosureFunction`执行完毕后就结束了。为什么还会对之后的结果产生影响呢? 我们可以通过反编译来看下编译器为我们做的事情。 为了增加可读性,下面的代码对编译器生成的名字进行修改,并对代码进行了适当的整理。 ```csharp class Program { sealed class DisplayClass { public int val; public int AnonymousFunction(int x) { return x + this.val; } } static void Main(string[] args) { Console.WriteLine(GetClosureFunction()(30)); } static Func
GetClosureFunction() { DisplayClass displayClass = new DisplayClass(); displayClass.val = 10; Func
internalAdd = displayClass.AnonymousFunction; Console.WriteLine(internalAdd(10)); displayClass.val = 30; Console.WriteLine(internalAdd(10)); return internalAdd; } } ``` 编译器创建了一个匿名类(如果不需要创建闭包,匿名函数只会是与`GetClosureFunction`生存在同一个类中,并且委托实例会被缓存,参见clr via C# 第四版362页),并在`GetClosureFunction`中创建了它实例。局部变量实际上是作为匿名类中的字段存在的。 ### 4.C#7对于不作为返回值的闭包的优化 如果在vs2017中编写第二节的代码。会得到一个提示,询问是否把lambda表达式(匿名函数)托转为本地函数。本地函数是c#7提供的一个新语法。那么使用本地函数实现闭包又会有什么区别呢? 如果还是第二节那样的代码,改成本地函数,查看IL代码。实际上不会发生任何变化。 ```csharp class Program { static void Main(string[] args) { Console.WriteLine(GetClosureFunction()(30)); } static Func
GetClosureFunction() { int val = 10; int InternalAdd(int x) => x + val; Console.WriteLine(InternalAdd(10)); val = 30; Console.WriteLine(InternalAdd(10)); return InternalAdd; } } ``` 但是当`internalAdd`不需要被返回时,结果就不一样了。 下面分别来看下匿名函数和本地函数创建不作为返回值的闭包的时候演示代码及经整理的反编译代码。 匿名函数 ```csharp static void GetClosureFunction() { int val = 10; Func
internalAdd = x => x + val; Console.WriteLine(internalAdd(10)); val = 30; Console.WriteLine(internalAdd(10)); } ``` 经整理的反编译代码 ```csharp sealed class DisplayClass { public int val; public int AnonymousFunction(int x) { return x + this.val; } } static void GetClosureFunction() { DisplayClass displayClass = new DisplayClass(); displayClass.val = 10; Func
internalAdd = displayClass.AnonymousFunction; Console.WriteLine(internalAdd(10)); displayClass.val = 30; Console.WriteLine(internalAdd(10)); } ``` 本地函数 ```csharp class Program { static void Main(string[] args) { } static void GetClosureFunction() { int val = 10; int InternalAdd(int x) => x + val; Console.WriteLine(InternalAdd(10)); val = 30; Console.WriteLine(InternalAdd(10)); } } ``` 经整理的反编译代码 ```csharp // 变化点1:由原来的class改为了struct struct DisplayClass { public int val; public int AnonymousFunction(int x) { return x + this.val; } } static void GetClosureFunction() { DisplayClass displayClass = new DisplayClass(); displayClass.val = 10; // 变化点2:不再构建委托实例,直接调用值类型的实例方法 Console.WriteLine(displayClass.AnonymousFunction(10)); displayClass.val = 30; Console.WriteLine(displayClass.AnonymousFunction(10)); } ``` 上述这两点变化在一定程度上能够带来性能的提升,目前的理解是,用结构体代替类,结构体实例能够在方法跑完后就立即释放,不需要等待垃圾回收,所以在官方的推荐中,如果委托的使用不是必要的,更推荐使用本地函数而非匿名函数。
这里⇓感觉得写点什么,要不显得有点空,但还没想好写什么...
返回顶部
About
京ICP备13038605号
© 代码片段 2025