1. ホーム
  2. c#

[解決済み] object-lifetime-extending-closure はC#コンパイラのバグか?

2022-06-17 09:02:38

質問

私は 質問 の質問に答えているときに、クロージャがオブジェクトの寿命を(合法的に)延長する可能性について、私はいくつかの 非常に 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 フィールドにオブジェクトの参照が入力されますが、決して読み込まれません (理由はありません)。

では、どうなっているのでしょうか?言語仕様はそれを許可しているのでしょうか。 これはコンパイラーのバグ/奇妙さなのか、それともクロージャーがオブジェクトを参照する正当な理由 (私が明らかに見逃しているもの) があるのでしょうか。これは、クロージャを好むプログラマ (私のような) が、知らず知らずのうちにプログラムに奇妙なメモリ リーク (デリゲートがイベント ハンドラとして使用された場合を想像してください) を導入するレシピのように見えるので、私を不安にさせます。

どのように解決するのですか?

それは確かにバグのように見えます。私に注意を促してくれてありがとうございます。調査してみます。すでに発見され、修正されている可能性もあります。