我正在回答一个与闭包可能(合法地)延长对象生命周期有关的问题,然后遇到了 C# 编译器(如果有影响则为 4.0 版本)的一些极其好奇的代码生成。
我能找到的最简复现代码如下:
1.创建一个 lambda 表达式,该表达式在调用包含类型的静态方法时捕获一个局部变量。 2.将生成的委托引用分配给包含对象的实例字段。
结果: 编译器创建了一个闭包对象,该闭包对象引用了创建 lambda 表达式的对象,尽管没有理由这么做 - 委托的"内部"目标是一个静态方法,并且在执行委托时不需要(也不会)访问 lambda 创建对象的实例成员。实际上,编译器的行为就像程序员无缘无故地捕获了
请注意,闭包对象的
那么这是怎么回事?语言规范允许吗?这是编译器的错误/怪异行为还是有一个良好的原因(我显然错过了),使闭包引用该对象?这让我感到焦虑,因为这看起来像是一个闭包程序员(像我一样)无意中会在程序中引入奇怪的内存泄漏(想象一下如果委托被用作事件处理程序)。
我能找到的最简复现代码如下:
1.创建一个 lambda 表达式,该表达式在调用包含类型的静态方法时捕获一个局部变量。 2.将生成的委托引用分配给包含对象的实例字段。
结果: 编译器创建了一个闭包对象,该闭包对象引用了创建 lambda 表达式的对象,尽管没有理由这么做 - 委托的"内部"目标是一个静态方法,并且在执行委托时不需要(也不会)访问 lambda 创建对象的实例成员。实际上,编译器的行为就像程序员无缘无故地捕获了
this
。class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
生成的发布版本代码(反编译为“简化”的C#)如下所示:
public void InstanceMethod()
{
<>c__DisplayClass1 CS$<>8__locals2 = new <>c__DisplayClass1();
CS$<>8__locals2.<>4__this = this; // What's this doing here?
CS$<>8__locals2.capturedVariable = Math.Pow(42.0, 1.0);
this._field = new Action(CS$<>8__locals2.<InstanceMethod>b__0);
}
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
// Fields
public Foo <>4__this; // Never read, only written to.
public double capturedVariable;
// Methods
public void <InstanceMethod>b__0()
{
Foo.StaticMethod(this.capturedVariable);
}
}
请注意,闭包对象的
<>4__this
字段被填充了一个对象引用,但从未被读取(没有理由)。那么这是怎么回事?语言规范允许吗?这是编译器的错误/怪异行为还是有一个良好的原因(我显然错过了),使闭包引用该对象?这让我感到焦虑,因为这看起来像是一个闭包程序员(像我一样)无意中会在程序中引入奇怪的内存泄漏(想象一下如果委托被用作事件处理程序)。