闭包的编译器生成代码解析:测试类与自动生成辅助类分析
闭包背后的编译器生成代码解析
嘿,咱们来一步步拆解这段C#闭包代码里编译器偷偷做的工作,搞清楚为什么那个局部变量x能一直保持状态~
先回顾原代码的核心行为
你写的CreateAdder方法里,局部变量x本来是方法栈上的临时变量,按道理CreateAdder执行完就该被销毁了。但因为匿名委托捕获了它,所以它的生命周期被延长了——每次调用返回的Adder委托,都会让x自增并返回最新值,三次调用输出1、2、3,这就是闭包的魔力。
编译器生成的辅助类到底是干啥的?
编译器自动生成的<>c__DisplayClass0_0类(带<>`的命名是编译器专属,避免和你的代码重名),核心作用就是把栈上的局部变量包装成引用类型的成员,具体细节:
[CompilerGenerated]标记明确告诉我们这是编译器造的类,不是你写的sealed修饰符是为了让编译器做更多优化(毕竟不需要继承这个类)- 里面的
public int x就是原代码里的局部变量x——因为值类型的局部变量在栈上,方法结束就没了,编译器把它移到这个类里,变成引用类型的实例成员,这样委托就能通过持有类的实例来持续访问它
匿名方法的转化
原代码里的匿名方法delegate { x++; return x; },编译器会在这个辅助类里生成一个对应的方法,比如命名为<CreateAdder>b__0(你看到的internal...就是这个方法)。这个方法的逻辑很简单:拿到当前辅助类实例的x,自增后返回,伪代码大概是这样:
internal int <CreateAdder>b__0() { this.x++; return this.x; }
CreateAdder方法的实际执行逻辑
编译器会把你写的CreateAdder重写成类似这样的逻辑:
static Adder CreateAdder() { // 创建辅助类的实例 <>c__DisplayClass0_0 displayClass = new <>c__DisplayClass0_0(); // 初始化原来的局部变量x displayClass.x = 0; // 返回绑定了辅助类实例和生成方法的委托 return new Adder(displayClass.<CreateAdder>b__0); }
为什么三次调用会输出1、2、3?
当你在Test方法里调用CreateAdder()时,会得到一个绑定了同一个辅助类实例的委托。每次调用add(),本质都是调用这个实例上的<CreateAdder>b__0方法,操作的是同一个x成员,所以每次自增都会保留状态,输出自然是1、2、3。
内容的提问来源于stack exchange,提问作者BennoDual
相关产品推荐
相关产品推荐

