我正在回答一个关于闭包(合法地)扩展对象生命周期的可能性的 有个问题问题,这时我在 C # 编译器中遇到了一些 非常奇怪的代码生成器(如果有问题的话,是4.0)。
我能找到的最短的复制品如下:
结果: 编译器创建了一个闭包对象来引用创建 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
字段用对象引用填充,但从不从中读取(没有原因)。
这是怎么回事?语言规范允许吗?这是一个编译器错误/怪异还是有一个很好的理由(我显然遗漏了)闭包引用该对象?这让我感到焦虑,因为这看起来像是一个让喜欢闭包的程序员(像我一样)不知不觉地将奇怪的内存泄漏(想象一下如果委托被用作事件处理程序)引入程序的处方。