323
string [] files = new string[2];
files[0] = "ThinkFarAhead.Example.Settings.Configuration_Local.xml";
files[1] = "ThinkFarAhead.Example.Settings.Configuration_Global.xml";

//Resharper complains this is an "access to modified closure"
for (int i = 0; i < files.Length; i++ )
{
    // Resharper disable AccessToModifiedClosure
    if(Array.Exists(Assembly.GetExecutingAssembly().GetManifestResourceNames(),
    delegate(string name) { return name.Equals(files[i]); }))
         return Assembly.GetExecutingAssembly().GetManifestResourceStream(files[i]);
    // ReSharper restore AccessToModifiedClosure
}

ReSharperはこれが「変更されたクロージャへのアクセス」であると不平を言っていますが、上記はうまく機能しているようです。誰でもこれに光を当てることができますか?

(このトピックはここに続きます)

4

3 に答える 3

319

この場合、実際にはループ内でデリゲートを実行しているので問題ありません。

ただし、デリゲートを保存して後で使用すると、ファイルにアクセスしようとすると、すべてのデリゲートが例外をスローすることがわかります[i] -デリゲート時の値ではなく変数 iをキャプチャしています創造。

要するに、潜在的な罠として注意すべきことですが、この場合は害はありません。

結果が直観に反する、より複雑な例については、このページの下部を参照してください。

于 2008-10-24T22:20:44.997 に答える
29

これは古い質問であることは承知していますが、最近クロージャを研究していて、コード サンプルが役に立つかもしれないと考えました。舞台裏で、コンパイラは関数呼び出しのレキシカル クロージャを表すクラスを生成しています。おそらく次のようになります。

private sealed class Closure
{
    public string[] files;
    public int i;

    public bool YourAnonymousMethod(string name)
    {
        return name.Equals(this.files[this.i]);
    }
}

前述のように、述語は作成直後に呼び出されるため、関数は機能します。コンパイラは次のようなものを生成します。

private string Works()
{
    var closure = new Closure();

    closure.files = new string[3];
    closure.files[0] = "notfoo";
    closure.files[1] = "bar";
    closure.files[2] = "notbaz";

    var arrayToSearch = new string[] { "foo", "bar", "baz" };

    //this works, because the predicates are being executed during the loop
    for (closure.i = 0; closure.i < closure.files.Length; closure.i++)
    {
        if (Array.Exists(arrayToSearch, closure.YourAnonymousMethod))
            return closure.files[closure.i];
    }

    return null;
}

一方、述語を格納してから後で呼び出すと、述語へのすべての呼び出しが実際にはクロージャー クラスの同じインスタンスで同じメソッドを呼び出すことになるため、同じ値を使用することがわかります。私。

于 2013-03-29T17:31:15.950 に答える