このオブジェクト寿命延長クロージャは C# コンパイラのバグですか? 質問する

このオブジェクト寿命延長クロージャは C# コンパイラのバグですか? 質問する

私は答えていました質問クロージャが(合法的に)オブジェクトの寿命を延長する可能性について、私はいくつかの非常にC# コンパイラ (4.0 が重要) による興味深いコード生成。

私が見つけた最も短い再現は次のとおりです。

  1. 呼び出し中にローカルをキャプチャするラムダを作成します。静的包含型のメソッド。
  2. 生成されたデリゲート参照を実例包含オブジェクトのフィールド。

結果:コンパイラは、ラムダを作成したオブジェクトを参照するクロージャオブジェクトを作成しますが、デリゲートの「内部」ターゲットは静的メソッドであり、ラムダ作成オブジェクトのインスタンス メンバーは、デリゲートの実行時に変更される必要はなく、変更されません。事実上、コンパイラは、プログラマが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

それは確かにバグのようです。ご指摘いただきありがとうございます。調べてみます。すでに発見され、修正されている可能性もあります。

おすすめ記事