Nesneler asla C ++ 'da olduğu gibi C # kapsam dışına çıkmaz. Artık kullanılmadıklarında Çöp Toplayıcı tarafından otomatik olarak ele alınırlar. Bu, bir değişkenin kapsamının tamamen deterministik olduğu C ++ 'dan daha karmaşık bir yaklaşımdır. CLR çöp toplayıcı, oluşturulan tüm nesnelerden aktif olarak geçer ve kullanılıyorsa çalışır.
Bir nesne bir işlevde "kapsam dışına çıkabilir" ancak değeri döndürülürse, GC çağrı işlevinin dönüş değeri üzerinde tutup tutmadığına bakar.
null
Çöp toplama, diğer nesneler tarafından hangi nesnelere başvurulduğunu çalışarak çalıştığından nesne başvurularını ayarlamak gereksizdir.
Uygulamada, yıkım konusunda endişelenmenize gerek yok, sadece çalışıyor ve harika :)
Dispose
IDisposable
onlarla çalışmayı bitirdiğinizde uygulanan tüm nesnelerde çağrılmalıdır . Normalde using
şu nesnelerle bir blok kullanırsınız :
using (var ms = new MemoryStream()) {
//...
}
EDIT Değişken kapsamda. Craig, değişken kapsamın nesne ömrü üzerinde herhangi bir etkisi olup olmadığını sordu. CLR'nin bu yönünü doğru bir şekilde açıklamak için, C ++ ve C # 'dan birkaç kavramı açıklamam gerekecek.
Gerçek değişken kapsamı
Her iki dilde de değişken yalnızca tanımlandığı şekilde kullanılabilir - sınıf, işlev veya parantez içine alınmış bir ifade bloğu. Ancak küçük fark, C # 'da değişkenlerin iç içe bir blokta yeniden tanımlanamamasıdır.
C ++ 'da, bu tamamen yasaldır:
int iVal = 8;
//iVal == 8
if (iVal == 8){
int iVal = 5;
//iVal == 5
}
//iVal == 8
Ancak C # 'da bir derleyici hatası alırsınız:
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
}
Oluşturulan MSIL'a bakarsanız bu mantıklıdır - işlev tarafından kullanılan tüm değişkenler işlevin başlangıcında tanımlanır. Bu işleve bir göz atın:
public static void Scope() {
int iVal = 8;
if(iVal == 8) {
int iVal2 = 5;
}
}
Aşağıda üretilen IL gösterilmektedir. İf bloğunun içinde tanımlanan iVal2'nin aslında işlev düzeyinde tanımlandığını unutmayın. Etkili bir şekilde bu, C # 'ın değişken ömür açısından sadece sınıf ve fonksiyon seviyesi kapsamına sahip olduğu anlamına gelir.
.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 ++ kapsamı ve nesne ömrü
Yığına ayrılan bir C ++ değişkeni, kapsam dışına çıkarsa tahrip olur. C ++ 'da yığın veya yığın üzerinde nesneler oluşturabileceğinizi unutmayın. Bunları yığın üzerinde oluşturduğunuzda, yürütme kapsamdan ayrıldığında, yığıntan atlar ve yok edilirler.
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 ++ nesneleri yığın üzerinde oluşturulduğunda, açıkça yok edilmelidir, aksi takdirde bir bellek sızıntısıdır. Ancak yığın değişkenleri ile böyle bir sorun yok.
C # Nesne Ömrü
CLR'de, nesneler (yani referans türleri) her zaman yönetilen yığın üzerinde oluşturulur. Bu, nesne oluşturma sözdizimi ile daha da güçlendirilir. Bu kod snippet'ini düşünün.
MyClass stackObj;
C ++ 'da bu MyClass
, yığın üzerinde bir örnek oluşturur ve varsayılan kurucusunu çağırır. C # 'da MyClass
hiçbir şeye işaret etmeyen sınıfa bir referans oluşturur . Sınıf örneği oluşturmanın tek yolu new
operatör kullanmaktır :
MyClass stackObj = new MyClass();
Bir şekilde, C # nesneleri, new
C ++ 'da sözdizimi kullanılarak oluşturulan nesnelere çok benzer - yığın üzerinde oluşturulurlar, ancak C ++ nesnelerinin aksine, çalışma zamanı tarafından yönetilirler, bu yüzden onları yok etme konusunda endişelenmenize gerek yoktur.
Nesneler her zaman öbek üzerinde olduğundan, nesne referanslarının (yani işaretçiler) kapsam dışına çıkması tartışmaya dönüşür. Bir nesnenin toplanıp toplanmayacağını belirlemede, nesneye yapılan referansların varlığından daha fazla faktör vardır.
C # Nesne başvuruları
Jon Skeet , Java'daki nesne referanslarını, nesne olan balona bağlı dize parçalarıyla karşılaştırdı . Aynı benzetme C # nesne başvuruları için de geçerlidir. Basitçe nesnenin bulunduğu yığının bir konumuna işaret ederler. Bu nedenle, null değerine ayarlamanın nesne ömrü üzerinde hemen bir etkisi yoktur, GC "patlayana" kadar balon var olmaya devam eder.
Balon benzetmesine devam edilirken, balonun kendisine bir dizi bağlı olmadığında yok edilebileceği mantıklı görünmektedir. Aslında, bu, sayılan nesnelerin tam olarak yönetilmeyen dillerde nasıl çalıştığıdır. Ancak bu yaklaşım dairesel referanslar için pek işe yaramaz. Bir dize ile birbirine bağlanmış ancak hiçbir balonun başka bir şeye sahip olmadığı iki balon düşünün. Basit ref sayma kurallarına göre, balon grubunun tamamı "yetim" olmasına rağmen her ikisi de var olmaya devam eder.
.NET nesneleri bir çatı altında helyum balonları gibidir. Çatı açıldığında (GC çalışır) - birlikte bağlı balon grupları olsa da, kullanılmayan balonlar uçup gider.
.NET GC, nesil GC ile markalama ve süpürme kombinasyonunu kullanır. Nesil yaklaşım, çalışma zamanının en son tahsis edilen nesneleri incelemek için tercih edilmesini içerir, çünkü kullanılma olasılıkları daha yüksektir ve işaretleme ve süpürme, çalışma zamanının tüm nesne grafiğinden geçmesini ve kullanılmayan nesne grupları varsa çalışmasını içerir. Bu, dairesel bağımlılık sorununu yeterince ele almaktadır.
Ayrıca, .NET GC başka bir iş parçacığında (sonlandırıcı iş parçacığı olarak da adlandırılır) çalışacak ve ana iş parçacığında bunu yapmak programınızı kesintiye uğratacaktır.