我正在回答 question关于闭包(合法地)延长对象生命周期的可能性,当我遇到一些非常好奇的 C# 编译器代码生成(如果重要的话是 4.0)。
我能找到的最短的重现如下:
- 创建一个在调用包含类型的static 方法时捕获本地的 lambda。
- 将生成的委托(delegate)引用分配给包含对象的实例字段。
结果:编译器创建一个引用创建 lambda 的对象的闭包对象,但它没有理由这样做 - 委托(delegate)的“内部”目标是一个静态方法,并且执行委托(delegate)时,不需要(也不会)触及 lambda-creating-object 的实例成员。实际上,编译器就像程序员捕获了 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
闭包对象的字段填充了一个对象引用,但从未被读取(没有原因)。
那么这里发生了什么?语言规范允许吗?这是一个编译器错误/奇怪还是有一个很好的理由(我显然没有)让闭包引用该对象?这让我很焦虑,因为这看起来像是喜欢闭包的程序员(比如我)无意中将奇怪的内存泄漏(想象一下如果委托(delegate)被用作事件处理程序)引入程序的秘诀。
最佳答案
这确实看起来像一个错误。谢谢你让我注意到它。我会调查一下。可能已经找到并修复了。
关于c# - 这个 object-lifetime-extending-closure 是 C# 编译器错误吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8419079/