Değiştirilmiş Kapatmaya Erişim


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 bunun "değiştirilmiş kapanışa erişim" olduğundan şikayetçi olsa da yukarıdakilerin işe yaradığı görülüyor. Birisi buna ışık tutabilir mi?

(bu konu burada devam etti )


6
Bağlantı kesildi, ancak WebArchive'da buldum: web.archive.org/web/20150326104221/http://www.jarloo.com/…
Eric Wu

Yanıtlar:


314

Bu durumda, delege döngü içinde yürüttüğünüz için sorun değil .

Temsilciyi kaydedip daha sonra kullanıyorsanız, tüm delegelerin dosyalara erişmeye çalışırken istisnalar atacağını görürsünüz [i] - delegeler zamanındaki değeri yerine değişkeni i yakalarlar oluşturma.

Kısacası, potansiyel bir tuzak olarak bilinmesi gereken bir şeydir , ancak bu durumda size zarar vermez.

Sonuçların sezgisel olduğu daha karmaşık bir örnek için bu sayfanın alt kısmına bakın .


29

Bunun eski bir soru olduğunu biliyorum, ama son zamanlarda kapanışları inceledim ve bir kod örneğinin yararlı olabileceğini düşündüm. Perde arkasında, derleyici işlev çağrınız için sözcüksel bir kapanışı temsil eden bir sınıf üretiyor. Muhtemelen şuna benzer:

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

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

Yukarıda belirtildiği gibi, işleviniz çalışır çünkü tahminler oluşturma işleminden hemen sonra çağrılır. Derleyici şöyle bir şey üretecektir:

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

Öte yandan, eğer saklar ve daha sonra tahminleri çağırırsanız, tahminlere yapılan her bir çağrının kapanış sınıfının aynı örneğinde gerçekten aynı yöntemi çağırdığını ve bu nedenle aynı değeri ben.


4

"files", anonim delege işlevi tarafından yakalandığı için yakalanan bir dış değişkendir . Anonim delege işlevi, ömrünü uzatır.

Yakalanan dış değişkenler Bir dış değişkene anonim bir işlev tarafından başvurulduğunda, dış değişkenin anonim işlev tarafından yakalandığı söylenir. Genellikle, yerel bir değişkenin ömrü ilişkili olduğu bloğun veya ifadenin yürütülmesi ile sınırlıdır (Yerel değişkenler). Ancak, yakalanan bir dış değişkenin ömrü en azından anonim işlevden oluşturulan temsilci veya ifade ağacı çöp toplama için uygun hale gelene kadar uzatılır.

MSDN'deki Dış Değişkenler

Yerel bir değişken veya bir değer parametresi anonim bir işlevle yakalandığında, yerel değişken veya parametre artık sabit bir değişken olarak kabul edilmez (Sabit ve taşınabilir değişkenler), ancak bunun yerine taşınabilir bir değişken olarak kabul edilir. Dolayısıyla, yakalanan bir dış değişkenin adresini alan güvenli olmayan tüm kodlar, değişkeni düzeltmek için önce sabit ifadeyi kullanmalıdır. Yakalanmamış bir değişkenin aksine, yakalanan bir yerel değişkenin aynı anda birden çok yürütme iş parçacığına maruz kalabileceğini unutmayın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.