问题描述
我正在回答一个关于关闭可能性的问题(合法地)当我在 C# 编译器方面遇到一些非常 奇怪的代码生成时,延长对象生命周期(4.0,如果这很重要).
I was answering a question about the possibility of closures (legitimately) extending object-lifetimes when I ran into some extremely curious code-gen on the part of the C# compiler (4.0 if that matters).
我能找到的最短的repro如下:
The shortest repro I can find is the following:
- 创建一个 lambda,在调用包含类型的 静态 方法时捕获本地.
- 将生成的委托引用分配给包含对象的实例字段.
- Create a lambda that captures a local while calling a static method of the containing type.
- Assign the generated delegate-reference to an instance field of the containing object.
结果:编译器创建一个引用创建 lambda 的对象的闭包对象,当它没有理由 - 委托的内部"目标是一个 静态 方法,并且lambda-creating-object 的实例成员在执行委托时不需要(也不会)被触及.实际上,编译器的行为就像程序员无缘无故地捕获了this
.
Result: The compiler creates a closure-object that references the object that created the lambda, when it has no reason to - the 'inner' target of the delegate is a static method, and the lambda-creating-object's instance members needn't be (and aren't) touched when the delegate is executed. Effectively, the compiler is acting like the programmer has captured this
without reason.
class Foo
{
private Action _field;
public void InstanceMethod()
{
var capturedVariable = Math.Pow(42, 1);
_field = () => StaticMethod(capturedVariable);
}
private static void StaticMethod(double arg) { }
}
从发布版本生成的代码(反编译为更简单的"C#)如下所示:
The generated code from a release build (decompiled to 'simpler' C#) looks like this:
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
字段填充了对象引用,但从未从中读取(没有理由).
Observe that <>4__this
field of the closure object is populated with an object reference but is never read from (there is no reason).
那么这里发生了什么?语言规范是否允许这样做?这是一个编译器错误/奇怪之处还是有一个很好的理由(我显然缺少)让闭包引用该对象?这让我很焦虑,因为这看起来像是让喜欢闭包的程序员(像我一样)不知不觉地将奇怪的内存泄漏(想象一下,如果委托用作事件处理程序)引入程序的秘诀.
So what's going on here? Does the language-specification allow for it? Is this a compiler bug / oddity or is there a good reason (that I'm clearly missing) for the closure to reference the object? This makes me anxious because this looks like a recipe for closure-happy programmers (like me) to unwittingly introduce strange memory-leaks (imagine if the delegate were used as an event-handler) into programs.
推荐答案
这看起来确实是个错误.感谢您引起我的注意.我会调查一下.可能已经找到并修复了.
That sure looks like a bug. Thanks for bringing it to my attention. I'll look into it. It is possible that it has already been found and fixed.
这篇关于这个对象生命周期扩展闭包是 C# 编译器错误吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!