当我遇到一些非常奇怪的C#编译器代码(4.0,如果重要的话)时,我正在回答一个关于闭合(合法地)延长对象生命周期的可能性的question .
我能找到的最短的repro如下:
-
在调用包含类型的静态方法时创建一个捕获局部的lambda .
-
将生成的委托引用分配给包含对象的实例字段 .
结果:编译器创建一个闭包对象,该对象引用创建lambda的对象,当它没有理由时 - 委托的'inner'目标是静态方法,并且lambda-creating-object 's instance members needn' t是(并且不是' t)代表执行时触及 . 实际上,编译器的行为就像程序员无缘无故地捕获了 this
.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
从发布版本生成的代码(反编译为'simpler'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
字段填充了对象引用,但从不读取(没有理由) .
那么这里发生了什么?语言规范是否允许它?这是一个编译器错误/奇怪或是否有一个很好的理由(我明显缺少)闭包引用对象?这让我很焦虑,因为这看起来像是一个关闭快乐程序员(像我一样)的秘诀,无意中将奇怪的内存泄漏(想象一下,如果委托被用作事件处理程序)到程序中 .
2 回答
这肯定看起来像一个bug . 谢谢你引起我的注意 . 我会调查一下 . 它可能已被发现并修复 .
这似乎是一个错误或不必要的:
我在IL lang中运行你的例子:
例2:
在cl :(注意!!现在这个参考已经消失!)
例3:
在IL中:(此指针返回)
在所有三种情况下,方法-b__0() - 看起来都是一样的:
在所有3种情况下都有一个静态方法的引用,因此它更奇怪 . 所以在经过这些分析之后,我会说它是一个错误/没有好处 . !