จะเกิดอะไรขึ้นเมื่อคุณกำหนดการอ้างอิงตัวแปรท้องถิ่นภายในวิธีการให้กับตัวแปรคงที่สาธารณะ

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

class Foo {}

class Program
{
    public static Foo someStaticVar;

    static void Main(string[] args)
    {
        Foo localVariable = new Foo();

        int x = 4;

        someStaticVar = localVariable; // is someStaticVariable still will have the same address?
    } 
    // variable x will be pushed of the stack
    // localVariable will be pushed of the stack
    // what happens then with someStatic var? 
}

ฉันยังได้เรียนรู้ด้วยว่าเมื่อประกาศตัวแปรภายในเมธอด ตัวแปรเหล่านั้นจะถูกพุชไปที่สแต็กเมื่อสร้างขึ้น และแตกสแต็กออกมาเมื่อเมธอดส่งคืน หากทั้งหมดนั้นเป็นจริง someStaticVar ก็ควรจะหายไป แต่ก็ไม่เป็นเช่นนั้น

ฉันแน่ใจว่าฉันต้องเข้าใจบางอย่างไม่ถูกต้อง หรือบางทีในบรรทัด someStaticVar = localVariable; กำลังทำการสำเนาวัตถุนั้นในเชิงลึก แต่ฉันสงสัยเพราะมีคำถามมากมายบนอินเทอร์เน็ตเกี่ยวกับวิธีการทำสำเนาวัตถุนั้นในเชิงลึก และพวกมันแตกต่างจากแนวทางนี้มาก


person Tono Nam    schedule 18.09.2012    source แหล่งที่มา
comment
ที่อยู่หน่วยความจำไม่มีความหมายในภาษาที่ได้รับการจัดการ ดังนั้น ไม่ คอมไพลเลอร์ไม่ได้กำหนดที่อยู่หน่วยความจำล่วงหน้าให้กับตัวแปรใดๆ ใน C#   -  person Peter Ritchie    schedule 18.09.2012


คำตอบ (4)


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

ต่อไปนี้คือสิ่งที่เกิดขึ้นทีละขั้นตอน:

static void Main(string[] args)
{
    // an instance of Foo is created and pushed on the heap
    // localVariable is now pointing to the address of this instance
    // localVariable itself is stored on the stack
    Foo localVariable = new Foo();

    // someStaticVar is now pointing to the same location on the heap as
    // the localVariable - the Foo instance created earlier
    someStaticVar = localVariable; 
} 
// at this stage the Main method finishes executing and all local
// variables are falling out of scope. But since you have a static variable
// pointing to the Foo instance that was created inside the Main method this
// instance is not illegible for garbage collection because there are still 
// references to it (someStaticVar).

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

person Darin Dimitrov    schedule 18.09.2012
comment
ฉันคิดว่ามันเป็นเรื่องที่ทำให้เข้าใจผิดที่จะแนะนำว่าอินสแตนซ์ของ Foo ถูกสร้างขึ้นบนสแต็ก - วัตถุนั้นถูกสร้างขึ้นบนฮีป การอ้างอิงจะถูกเก็บไว้ในสแต็ก - person Dan Puzey; 18.09.2012
comment
@DanPuzey คุณพูดถูกอย่างแน่นอน ขอบคุณสำหรับการชี้ให้เห็นสิ่งนี้ ฉันได้อัปเดตคำตอบเพื่อคำนึงถึงคำพูดของคุณ - person Darin Dimitrov; 18.09.2012

เมื่อประกาศตัวแปรภายในวิธีการตัวแปรเหล่านั้นจะถูกผลักไปที่สแต็กเมื่อสร้างและแตกออกมาในสแต็กเมื่อวิธีการส่งคืน

Foo localVariable = ใหม่ Foo();

จะสร้างวัตถุ Foo บนฮีป และการอ้างอิงจะถูกเก็บไว้ในสแต็ก เมื่อวิธีการเสร็จสิ้น การอ้างอิงจะถูกลบออกจากสแต็ก ตัวรวบรวมขยะจะดำเนินการลบ Foo ออกจากฮีป เนื่องจากจะไม่มีการอ้างอิงถึงออบเจ็กต์ Foo แต่,

someStaticVar = localVariable;

จะทำให้ someStaticVar อ้างอิงวัตถุ Foo บนฮีป แม้หลังจากออกจากเมธอดแล้ว someStaticVar จะยังคงอ้างอิงอ็อบเจ็กต์ Foo ดังนั้นคนเก็บขยะจะไม่รวบรวมวัตถุ Foo นั้น สิ่งสำคัญที่ต้องจำคือ เมื่อสร้างออบเจ็กต์ประเภทการอ้างอิง ออบเจ็กต์จะถูกสร้างขึ้นบนฮีป และการอ้างอิงจะถูกเก็บไว้ในสแต็ก

คำถามคือ "ที่จัดเก็บฟิลด์คงที่" ฟิลด์อินสแตนซ์ของออบเจ็กต์ถูกจัดเก็บบนฮีป ตัวแปรภายในเครื่องจะถูกจัดเก็บบนสแต็ก แต่ที่ "ตัวแปรคงที่มีอยู่ในหน่วยความจำ""

person amit gaikwad    schedule 18.09.2012

ไม่มีการคัดลอกวัตถุในเชิงลึก: ตัววัตถุเองจะถูกจัดเก็บไว้ในฮีป ไม่ใช่บนสแต็ก คุณถูกต้องว่า someStaticVariable จะมีที่อยู่เดียวกันเสมอ ฉันเชื่อว่าความเข้าใจผิดของคุณอยู่ในสิ่งที่จัดเก็บตามที่อยู่นั้น

เมื่อคุณประกาศตัวแปรประเภทการอ้างอิง (ประเภท object ใดๆ เช่น Foo ในโค้ดของคุณ) ตัวตัวแปรนั้นไม่ใช่วัตถุ แต่เป็นตัวแปรที่เก็บ ที่อยู่ ของวัตถุของคุณ ที่อยู่นั้นเป็นเพียงตัวเลข ดังนั้น "ที่อยู่ของ someStaticVariable" ไม่ใช่ที่อยู่ของ Foo เอง เป็น ที่อยู่ของที่อยู่ ของ Foo

ในทำนองเดียวกัน นี่คือสาเหตุที่ someStaticVar ของคุณไม่ "หายไป" สิ่งที่เกิดขึ้นในโค้ดของคุณคือ Foo ถูกสร้างขึ้นที่บางที่อยู่ในหน่วยความจำ และ localVariable ถูกตั้งค่าเป็นค่าที่แสดงถึง ที่อยู่ในหน่วยความจำ ของ Foo เมื่อคุณดำเนินการ someStaticVar = localVariable สิ่งที่เกิดขึ้นคือ ที่อยู่ จะถูกคัดลอกจาก localVariable ถึง someStaticVar ดังนั้นตัวแปรทั้งสองจึงชี้ไปที่ Foo เดียวกัน เมื่อ localVariable หายไป someStaticVar จะไม่ได้รับผลกระทบ

person Dan Puzey    schedule 18.09.2012

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

เมื่อบอกว่าตัวแปรคงที่มีที่อยู่หน่วยความจำคงที่ ไม่ จะบอกว่าค่าของที่อยู่หน่วยความจำนั้นเป็นค่าคงที่ ที่อยู่ของตัวชี้ไปยังวัตถุ Foo จะไม่เปลี่ยนแปลง แต่หมายเลข (ในกรณีนี้คือที่อยู่) ที่ถูกจัดเก็บไว้ในตำแหน่งหน่วยความจำคงที่นั้นอาจแตกต่างกันได้อย่างง่ายดายระหว่างวัตถุ Foo ต่างๆ ที่เป็นไปได้ (หรือ null)

ยิ่งไปกว่านั้น เนื่องจาก C# เป็นภาษาที่มีการจัดการและไม่ใช่ภาษาที่ไม่มีการจัดการ จึงไม่เป็นความจริงเลยที่จะบอกว่าตัวแปรคงที่จะมีที่อยู่หน่วยความจำคงที่ เป็นไปได้อย่างแน่นอน แต่จริงๆ แล้วมันเป็นรายละเอียดการใช้งานของตัวจัดการหน่วยความจำของ C# ที่คุณไม่ควรเชื่อถือ

person Servy    schedule 18.09.2012