访问修改后的闭包


316
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抱怨这是“访问修改后的闭包”,但上述方法似乎运行良好。有人可以阐明这一点吗?

(此主题此继续)


6
链接已经出来了,但我发现它在WebArchive:web.archive.org/web/20150326104221/http://www.jarloo.com/...
埃里克·吴

Answers:


314

在这种情况下,可以,因为您实际上是循环执行委托。

但是,如果您保存委托并在以后使用它,则会发现所有委托在尝试访问文件时都会引发异常[i]-他们捕获变量 i而不是委托时获取其值创建。

简而言之,这是一个潜在的陷阱,但在这种情况下,它不会伤害您。

有关结果与直觉相反的更复杂示例,请参见本页底部


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;
}

另一方面,如果要存储然后在以后调用谓词,则将看到对谓词的每个调用实际上都将在闭包类的相同实例上调用相同的方法,因此将对相同的值使用相同的值。一世。


4

“文件”是捕获的外部变量,因为它已由匿名委托函数捕获。通过匿名委托函数可以延长其寿命。

捕获的外部变量当匿名函数引用外部变量时,该外部变量被称为已被匿名函数捕获。通常,局部变量的生存期仅限于与其关联的块或语句的执行(局部变量)。但是,捕获的外部变量的生存期至少会延长,直到从匿名函数创建的委托或表达式树变为有资格进行垃圾回收为止。

MSDN上的外部变量

当匿名变量捕获局部变量或值参数时,该局部变量或参数不再被视为固定变量(固定和可移动变量),而是被视为可移动变量。因此,任何获取已捕获外部变量地址的不安全代码都必须首先使用fixed语句来修复变量。注意,与未捕获的变量不同,捕获的局部变量可以同时暴露给多个执行线程。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.