我当时回答的问题有关闭的可能(合法)扩展对象的生命周期,当我遇到了一些非常奇怪的代码生成的C#编译器的部分(4.0,如果该事项)。
我能找到的最短的再现是:
- 创建一个lambda来捕获本地,同时调用包含类型的 静态方法。
- 将生成的委托引用分配给包含对象的实例字段。
结果:编译器在没有理由的情况下创建了一个引用创建lambda的对象的闭包对象-委托的“内部”目标是静态方法,而lambda-createing对象的实例成员不需要执行委托时(不被)触碰。实际上,编译器的行为就像程序员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
,闭包对象的字段填充有对象引用,但从未读取过(没有理由)。
那么这是怎么回事?语言规范是否允许?这是一个编译器错误/奇怪之处,还是闭包引用该对象有充分的理由(我很明显缺少)?这让我感到焦虑,因为这看起来像是让喜欢闭包的程序员(像我一样)无意间将奇怪的内存泄漏(想象一下,如果将委托人用作事件处理程序)引入程序的方法。
我无法使用VS11 Developer预览对此进行复制。可以在VS2010SP1中复制。似乎是固定的:)
—
leppie 2011年
在VS2008SP1中也会发生这种情况。对于VS2010SP1,它适用于3.5和4.0。
—
leppie 2011年
嗯,错误是个大词。编译器只会生成效率低下的代码。当然不是泄漏,此垃圾收集没有问题。当他们致力于异步实现时,它可能已修复。
—
汉斯·帕桑
@Hans,如果委托可以在对象的生存期内生存下来,那么这将不会毫无问题地进行垃圾收集,并且没有什么可以阻止这种情况的发生。
—
SoftMemes
this
。