私は答えていました質問クロージャが(合法的に)オブジェクトの寿命を延長する可能性について、私はいくつかの非常にC# コンパイラ (4.0 が重要) による興味深いコード生成。
私が見つけた最も短い再現は次のとおりです。
- 呼び出し中にローカルをキャプチャするラムダを作成します。静的包含型のメソッド。
- 生成されたデリゲート参照を実例包含オブジェクトのフィールド。
結果:コンパイラは、ラムダを作成したオブジェクトを参照するクロージャオブジェクトを作成しますが、デリゲートの「内部」ターゲットは静的メソッドであり、ラムダ作成オブジェクトのインスタンス メンバーは、デリゲートの実行時に変更される必要はなく、変更されません。事実上、コンパイラは、プログラマが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
にオブジェクト参照が入力されていますが、そこから読み取られることはありません (理由はありません)。
では、ここで何が起こっているのでしょうか? 言語仕様ではそれが許されているのでしょうか? これはコンパイラのバグ/異常なのでしょうか、それともクロージャがオブジェクトを参照する正当な理由 (私が明らかに見逃している) があるのでしょうか? これは、クロージャを多用するプログラマー (私のような) が、プログラムに奇妙なメモリ リーク (デリゲートがイベント ハンドラーとして使用された場合を想像してください) を無意識のうちに導入するレシピのように見えるため、不安になります。
ベストアンサー1
それは確かにバグのようです。ご指摘いただきありがとうございます。調べてみます。すでに発見され、修正されている可能性もあります。