วัตถุไม่เคยออกนอกขอบเขตใน C # ตามที่ทำใน C ++ พวกเขาจะได้รับการจัดการโดย Garbage Collector โดยอัตโนมัติเมื่อไม่ได้ใช้งานอีกต่อไป นี่เป็นวิธีที่ซับซ้อนกว่า C ++ ซึ่งขอบเขตของตัวแปรนั้นถูกกำหนดไว้อย่างสมบูรณ์ ตัวรวบรวมขยะ CLR จะดำเนินการผ่านวัตถุทั้งหมดที่สร้างขึ้นและทำงานอย่างถูกต้องหากใช้งานอยู่
วัตถุสามารถ "เกินขอบเขต" ในฟังก์ชันเดียว แต่ถ้าส่งคืนค่าของมัน GC จะดูว่าฟังก์ชันการเรียกเก็บค่าการส่งคืนหรือไม่
การตั้งค่าการอ้างอิงวัตถุnull
เป็นสิ่งที่ไม่จำเป็นเนื่องจากการรวบรวมขยะทำงานโดยการหาวัตถุที่ถูกอ้างอิงโดยวัตถุอื่น
ในทางปฏิบัติคุณไม่ต้องกังวลกับการทำลายมันใช้งานได้และมันยอดเยี่ยม :)
Dispose
ต้องถูกเรียกบนวัตถุทั้งหมดที่นำมาใช้IDisposable
เมื่อคุณทำงานกับมันเสร็จแล้ว โดยปกติแล้วคุณจะใช้using
บล็อกกับวัตถุเหล่านั้นเช่น:
using (var ms = new MemoryStream()) {
//...
}
แก้ไขในขอบเขตตัวแปร เครกถามว่าขอบเขตของตัวแปรมีผลกับอายุการใช้งานของวัตถุหรือไม่ ในการอธิบายลักษณะของ CLR อย่างถูกต้องฉันจะต้องอธิบายแนวคิดบางอย่างจาก C ++ และ C #
ขอบเขตตัวแปรที่แท้จริง
ในทั้งสองภาษาตัวแปรสามารถใช้ได้ในขอบเขตเดียวกับที่กำหนดไว้ - คลาส, ฟังก์ชันหรือบล็อกคำสั่งที่ล้อมรอบด้วยเครื่องหมายปีกกา อย่างไรก็ตามความแตกต่างเล็กน้อยคือใน C # ตัวแปรไม่สามารถนิยามใหม่ในบล็อกซ้อน
ใน C ++ สิ่งนี้ถูกกฎหมายอย่างสมบูรณ์:
int iVal = 8;
//iVal == 8
if (iVal == 8){
int iVal = 5;
//iVal == 5
}
//iVal == 8
ใน C # คุณจะได้รับข้อผิดพลาดของคอมไพเลอร์ aa:
int iVal = 8;
if(iVal == 8) {
int iVal = 5; //error CS0136: A local variable named 'iVal' cannot be declared in this scope because it would give a different meaning to 'iVal', which is already used in a 'parent or current' scope to denote something else
}
สิ่งนี้สมเหตุสมผลถ้าคุณดูที่สร้าง MSIL - ตัวแปรทั้งหมดที่ใช้โดยฟังก์ชันถูกกำหนดไว้ที่จุดเริ่มต้นของฟังก์ชัน ดูฟังก์ชั่นนี้:
public static void Scope() {
int iVal = 8;
if(iVal == 8) {
int iVal2 = 5;
}
}
ด้านล่างเป็น IL ที่สร้างขึ้น โปรดทราบว่า iVal2 ซึ่งมีการกำหนดไว้ภายในบล็อก if ถูกกำหนดที่ระดับฟังก์ชั่นจริง ๆ อย่างมีประสิทธิภาพซึ่งหมายความว่า C # มีขอบเขตระดับคลาสและฟังก์ชันเท่าที่เกี่ยวข้องกับอายุการใช้งานของตัวแปร
.method public hidebysig static void Scope() cil managed
{
// Code size 19 (0x13)
.maxstack 2
.locals init ([0] int32 iVal,
[1] int32 iVal2,
[2] bool CS$4$0000)
//Function IL - omitted
} // end of method Test2::Scope
ขอบเขต C ++ และอายุการใช้งานวัตถุ
เมื่อใดก็ตามที่ตัวแปร C ++ ที่จัดสรรไว้ในสแต็กจะอยู่นอกขอบเขตที่มันจะถูกทำลาย โปรดจำไว้ว่าใน C ++ คุณสามารถสร้างวัตถุบนสแต็กหรือบนกอง เมื่อคุณสร้างมันลงบนสแต็กเมื่อการประมวลผลออกจากขอบเขตพวกมันจะถูกแตกออกจากสแต็กและถูกทำลาย
if (true) {
MyClass stackObj; //created on the stack
MyClass heapObj = new MyClass(); //created on the heap
obj.doSomething();
} //<-- stackObj is destroyed
//heapObj still lives
เมื่อวัตถุ C ++ ถูกสร้างขึ้นบนฮีปวัตถุเหล่านั้นจะต้องถูกทำลายอย่างชัดเจนไม่เช่นนั้นจะเป็นการรั่วไหลของหน่วยความจำ ไม่มีปัญหาดังกล่าวกับตัวแปรสแต็ค
C # อายุการใช้งานวัตถุ
ใน CLR, วัตถุ (ประเภทอ้างอิง IE) จะถูกเสมอสร้างขึ้นบนกองการจัดการ นี่คือการเสริมเพิ่มเติมโดยไวยากรณ์การสร้างวัตถุ พิจารณาข้อมูลโค้ดนี้
MyClass stackObj;
ใน C ++ สิ่งนี้จะสร้างอินสแตนซ์บนMyClass
สแต็กและเรียกคอนสตรัคเตอร์เริ่มต้น ใน C # มันจะสร้างการอ้างอิงถึงคลาสMyClass
ที่ไม่ได้ชี้ไปที่อะไรเลย วิธีเดียวในการสร้างตัวอย่างของคลาสคือการใช้new
โอเปอเรเตอร์:
MyClass stackObj = new MyClass();
ในทางวัตถุ C # นั้นเหมือนกับวัตถุที่สร้างขึ้นโดยใช้new
ไวยากรณ์ใน C ++ ซึ่งถูกสร้างขึ้นบน heap แต่ต่างจากวัตถุ C ++ พวกมันถูกจัดการโดย runtime ดังนั้นคุณไม่ต้องกังวลกับการทำลายมัน
เนื่องจากวัตถุอยู่บนกองเสมอความจริงที่ว่าการอ้างอิงวัตถุ (เช่นตัวชี้) ออกนอกขอบเขตกลายเป็นที่สงสัย มีหลายปัจจัยที่เกี่ยวข้องในการพิจารณาว่าวัตถุจะถูกเก็บรวบรวมมากกว่าเพียงแค่การแสดงตนของการอ้างอิงไปยังวัตถุ
การอ้างอิงวัตถุ C #
Jon Skeet เปรียบเทียบการอ้างอิงวัตถุใน Javaกับชิ้นส่วนของสตริงที่แนบมากับบอลลูนซึ่งเป็นวัตถุ คล้ายคลึงกันนำไปใช้กับการอ้างอิงวัตถุ C # พวกเขาเพียงแค่ชี้ไปที่ตำแหน่งของกองที่มีวัตถุ ดังนั้นการตั้งค่าให้เป็นโมฆะจะไม่มีผลทันทีต่ออายุการใช้งานของวัตถุบอลลูนยังคงมีอยู่จนกว่า GC จะ "ปรากฏ"
ต่อจากการเปรียบเทียบบอลลูนดูเหมือนว่ามีเหตุผลว่าเมื่อบอลลูนไม่มีสายอักขระติดอยู่จะสามารถทำลายได้ ในความเป็นจริงนี้เป็นสิ่งที่วัตถุอ้างอิงนับทำงานในภาษาที่ไม่มีการจัดการ ยกเว้นวิธีการนี้ใช้ไม่ได้ผลกับการอ้างอิงแบบวงกลมได้เป็นอย่างดี ลองนึกภาพลูกโป่งสองลูกที่ติดอยู่ด้วยกันด้วยสาย แต่บอลลูนทั้งสองไม่มีสายไปยังสิ่งอื่น ภายใต้กฎการนับการอ้างอิงอย่างง่ายพวกเขาทั้งคู่ยังคงมีอยู่ต่อไปแม้ว่ากลุ่มบอลลูนทั้งหมดจะเป็น "เด็กกำพร้า"
วัตถุ. NET นั้นเหมือนกับบอลลูนฮีเลียมใต้หลังคา เมื่อหลังคาเปิด (GC วิ่ง) - ลูกโป่งที่ไม่ได้ใช้จะลอยไปแม้ว่าอาจจะมีกลุ่มของบอลลูนที่ถูกโยงเข้าด้วยกัน
.NET GC ใช้การรวมกันของ generational GC และทำเครื่องหมายและกวาด Generational approach เกี่ยวข้องกับ runtime ที่นิยมใช้ในการตรวจสอบวัตถุที่ได้รับการจัดสรรเมื่อเร็ว ๆ นี้เนื่องจากพวกมันมีแนวโน้มที่จะไม่ได้ใช้งานมากขึ้นและการทำเครื่องหมายและการกวาดเกี่ยวข้องกับ runtime ที่ดำเนินการผ่านกราฟวัตถุทั้งหมด เรื่องนี้เกี่ยวข้องกับปัญหาการพึ่งพาแบบวงกลมอย่างเพียงพอ
นอกจากนี้. NET GC จะทำงานบนเธรดอื่น (เรียกว่าเธรด finalizer) เนื่องจากมีบิตที่ต้องทำและทำเช่นนั้นบนเธรดหลักจะขัดจังหวะโปรแกรมของคุณ