การเข้าถึงการปิดการแก้ไข


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/…
Eric Wu

คำตอบ:


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

"files" เป็นตัวแปรด้านนอกที่ถูกจับภาพเพราะฟังก์ชั่นของตัวแทนที่ไม่ระบุชื่อถูกจับได้ อายุการใช้งานจะถูกขยายโดยฟังก์ชั่นการมอบหมายที่ไม่ระบุชื่อ

บันทึกตัวแปรด้านนอกเมื่อตัวแปรภายนอกถูกอ้างอิงโดยฟังก์ชันที่ไม่ระบุตัวตนตัวแปรภายนอกถูกกล่าวถึงถูกจับโดยฟังก์ชันที่ไม่ระบุชื่อ ตามปกติแล้วอายุการใช้งานของตัวแปรโลคัลจะ จำกัด อยู่ที่การเรียกใช้งานบล็อกหรือข้อความสั่งที่สัมพันธ์กัน (ตัวแปรโลคัล) อย่างไรก็ตามอายุการใช้งานของตัวแปรภายนอกที่บันทึกไว้จะถูกขยายออกไปอย่างน้อยก็จนกว่าต้นไม้ผู้รับมอบสิทธิ์หรือทรีนิพจน์ที่สร้างจากฟังก์ชันที่ไม่ระบุชื่อจะมีสิทธิ์ใช้งานการรวบรวมขยะ

ตัวแปรภายนอกบน MSDN

เมื่อตัวแปรโลคัลหรือพารามิเตอร์ค่าถูกจับโดยฟังก์ชันที่ไม่ระบุชื่อตัวแปรหรือพารามิเตอร์โลคัลจะไม่ถูกพิจารณาว่าเป็นตัวแปรคงที่ (ตัวแปรถาวรและตัวแปรที่เคลื่อนย้ายได้) แต่ถูกพิจารณาว่าเป็นตัวแปรที่เคลื่อนย้ายได้ ดังนั้นรหัสที่ไม่ปลอดภัยใด ๆ ที่ใช้ที่อยู่ของตัวแปรภายนอกที่ถูกจับจะต้องใช้คำสั่งคงที่เพื่อแก้ไขตัวแปรก่อน โปรดทราบว่าไม่เหมือนกับตัวแปรที่ไม่ได้รับการปรับแต่งตัวแปรโลคอลที่จับสามารถสัมผัสกับเธรดการทำงานหลายเธรดได้พร้อมกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.