.NET'te kullanıldıktan sonra Nesneleri Boş / Hiçbir Şey Olarak Ayarlama


187

İşiniz bittiğinde tüm nesneleri null( NothingVB.NET'te) olarak ayarlamanız gerekir mi?

.NET'te IDisposablebazı kaynakları serbest bırakmak için arabirimi uygulayan nesnelerin herhangi bir örneğini atmanın gerekli olduğunu anlıyorum, ancak nesne atandıktan sonra hala bir şey olabilir (dolayısıyla isDisposedformlardaki özellik), bu yüzden hala ikamet edebileceğini varsayıyorum bellekte mi yoksa en azından kısmen mi?

Ayrıca, bir nesne kapsam dışına çıktığında, çöp toplayıcısının bir sonraki geçişine hazır toplama için işaretlendiğini biliyorum (bu zaman alabilir).

Bunu akılda tutarak null, sistemi artık kapsamda olmadığını ve herhangi bir kötü yan etkisi olup olmadığını çözmek zorunda kalmadan belleği serbest bırakmak için hızlandırmak için ayarlayacaktır.

MSDN makaleleri bunu örneklerde asla yapmaz ve şu anda zararı göremediğim için yapıyorum. Ancak herhangi bir yorum yararlı bu yüzden bir fikir karışımı rastladım.


4
+1 harika soru. Herkes derleyicinin ödevi tamamen optimize edeceği bir durumu biliyor mu? yani herhangi biri farklı koşullar altında MSIL'e bakmış ve bir nesneyi null değerine (veya eksikliğine) ayarladığından IL not etmiştir.
Tim Medora

Yanıtlar:


73

Karl kesinlikle doğrudur, kullandıktan sonra nesneleri null değerine ayarlamaya gerek yoktur. Bir nesne uygulanırsa IDisposable, IDisposable.Dispose()o nesneyle işiniz bittiğinde aradığınızdan emin olun (bir try.. finallyveya bir using()bloğa sarılır ). Ancak aramayı hatırlamasanız bile Dispose(), nesne üzerindeki sonlandırıcı yöntemi Dispose()sizi çağırıyor olmalıdır .

Bunun iyi bir tedavi olduğunu düşündüm:

IDisposable içine kazma

ve bu

IDisposable'ı Anlamak

GC ve yönetim stratejilerini ikinci kez tahmin etmeye çalışmanın bir anlamı yoktur çünkü kendi kendini ayarlayan ve opaktır. Dot Net Rocks üzerinde Jeffrey Richter ile iç çalışmalar hakkında iyi bir tartışma vardı: Windows Bellek Modeli ve Richters C # 20 numaralı CLR kitabı Jeffrey Richter harika bir tedavi görüyor:


6
Null değerine ayarlanmama kuralı "sert ve hızlı" değildir ... nesne büyük nesne yığınına konursa (boyut> 85K'dır), işiniz bittiğinde nesneyi null değerine ayarlarsanız GC'ye yardımcı olur kullanarak.
Scott Dorman

Sınırlı bir ölçüde katılıyorum, ancak bellek baskısı yaşamaya başlamadığınız sürece, nesneleri kullandıktan sonra null değerine ayarlayarak 'erken optimize etmeye' gerek duymuyorum.
Kev

21
"Zamanından önce optimize etmeyin" gibi tüm bu "iş zamanından önce optimize etmeyin" işi, CPU'ların hızlandığından ve CRUD uygulamalarının hıza ihtiyaç duymadığı için endişelenmeyin. Yine de ben olabilirim. :)
BobbyShaftoe

19
Asıl anlamı "Çöp Toplayıcı, hafızayı yönetmekte senden daha iyidir." Gerçi sadece ben olabilirim. :)
BobRodes

2
@BobbyShaftoe: "Erken optimizasyon kötüdür, her zaman" demek yanlıştır, çünkü "daha yavaş" tercih et "gibi ters seslere atlamaktır. Hiçbir makul programcı da söylemez. Nüans ve optimizasyonunuzun ne olduğu konusunda akıllı olmakla ilgilidir. Şahsen kod netliği ve GERÇEKTEN TEST performansından endişe ediyorum. okunabilirlik tamamen çekilirken 100.000 yinelemede.
Brent Rittenhouse

36

Nesneleri, işiniz bittiğinde null değerine ayarlamamanın bir başka nedeni de, nesneleri daha uzun süre canlı tutabilmesidir.

Örneğin

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is now eligible for garbage collection         

    // ... rest of method not using 'someType' ...
}

someType tarafından atıfta bulunulan nesnenin "DoSomething" çağrısından sonra GC'd olmasına izin verir, ancak

void foo()
{
    var someType = new SomeType();
    someType.DoSomething();
    // someType is NOT eligible for garbage collection yet
    // because that variable is used at the end of the method         

    // ... rest of method not using 'someType' ...
    someType = null;
}

bazen yöntemin sonuna kadar nesneyi canlı tutabilir. Tam zamanında genellikle boş atama uzak optimize edilmiş aynı olmak kadar kod ucunun her iki biti, yani.


Bu ilginç bir nokta. Her zaman nesnelerin, kapsamlandırıldıkları yöntem tamamlanana kadar kapsam dışına çıkmadığını düşündüm. Tabii ki nesne bir Kullanma bloğu içinde yer almıyorsa veya açıkça Hiçbir şey veya null olarak ayarlanmamışsa.
Guru Josh

1
Hayatta kalmasını sağlamak için tercih edilen bir yol kullanmaktır GC.KeepAlive(someType); bakın ericlippert.com/2013/06/10/construction-destruction
NotMe

14

Hayır null nesneler yok. Daha fazla bilgi için http://codebetter.com/blogs/karlseguin/archive/2008/04/27/foundations-of-programming-pt-7-back-to-basics-memory.aspx adresini ziyaret edebilirsiniz, ancak işleri ayarlayabilirsiniz. null değerine kodunuzu kirli dışında hiçbir şey yapmaz.


1
Paylaşılan bağlantıda bellek hakkında güzel ve ayrıntılı açıklama
user2323308

Bağlantı koptu. Bağlantılı içerik olmadan, bu cevap oldukça kullanışlıdır ve silinmelidir.
Imre Pühvel

7

Ayrıca:

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

7

Genel olarak, kullanımdan sonra nesneleri sıfırlamaya gerek yoktur, ancak bazı durumlarda bunun iyi bir uygulama olduğunu düşünüyorum.

Bir nesne IDisposable'ı uygularsa ve bir alanda saklanırsa, atılan nesneyi kullanmaktan kaçınmak için boş bırakmanın iyi olduğunu düşünüyorum. Aşağıdaki türden böcekler acı verici olabilir:

this.myField.Dispose();
// ... at some later time
this.myField.DoSomething();

Alanı bertaraf ettikten sonra sıfırlamak ve alanın tekrar kullanıldığı satırdan bir NullPtrEx almak iyidir. Aksi takdirde, satırda bazı şifreli hatalarla karşılaşabilirsiniz (DoSomething'in tam olarak ne yaptığına bağlı olarak).


8
Peki, atılmış bir nesne zaten atılmışsa ObjectDisposedException öğesini atmalıdır. Bu, bildiğim kadarıyla, her yerde demirbaş kodu gerektiriyor, ancak yine de, Disposed yine de kötü düşünülmüş bir paradigma.
nicodemus13

3
İçin Ctrl + F .Dispose(). Eğer bulursanız, IDisposable'ı doğru kullanmıyorsunuz demektir. Tek kullanımlık bir nesne için tek kullanımlık bir kullanma bloğunun sınırlarında olmalıdır. Ve kullanım bloğundan sonra myFieldartık erişime bile sahip değilsiniz . Ve kullanma bloğu içinde, ayarı nullgerekli değildir, using bloğu nesneyi sizin için atacaktır.
Suamere

7

nullDeğişkenlere ihtiyacınız olduğunu düşünüyorsanız, kodunuzun yeterince sıkı yapılandırılmamış olması muhtemeldir.

Bir değişkenin kapsamını sınırlamanın birkaç yolu vardır:

Steve Tranby tarafından belirtildiği gibi

using(SomeObject object = new SomeObject()) 
{
  // do stuff with the object
}
// the object will be disposed of

Benzer şekilde, basit köşeli parantezleri kullanabilirsiniz:

{
    // Declare the variable and use it
    SomeObject object = new SomeObject()
}
// The variable is no longer available

Ben gerçekten kodu temizlemek ve daha anlaşılır hale yardımcı olmak için herhangi bir "başlık" olmadan kıvırcık parantez kullanarak bulmak.


Özel yerel kapsamları bir kez (çoğunlukla bir smarta $$ olmak) kullanarak çalıştı. Şirket patladı.
Suamere

Başka bir not: Bunun nedeni, c # derleyicisinin IDisposable uygulayan yerel kapsamlı değişkenleri bulması ve kapsamı sona erdiğinde. Ancak ... SQL Bağlantıları .Dispose () hiçbir zaman en iyi duruma getirilmediğinde büyük bir zamandır. Açık dikkat gerektiren bazı türler vardır, bu yüzden kişisel olarak her zaman açıkça şeyler yaparım, böylece ısırılmam.
Suamere

5

Bir değişkeni null olarak ayarlamanız gereken tek zaman, değişkenin kapsam dışına çıkmaması ve artık onunla ilişkili verilere gereksinim duymamanızdır. Aksi takdirde gerek yoktur.


2
Bu doğru, ancak aynı zamanda muhtemelen kodunuzu yeniden düzenlemeniz gerektiği anlamına gelir. Amaçlanan kapsamın dışında bir değişken bildirmem gerektiğini hiç sanmıyorum.
Karl Seguin

2
"Değişken" in nesne alanlarını içerdiği anlaşılırsa, bu cevap çok mantıklıdır. "Değişkenin" yalnızca "yerel değişken" (bir yöntemin) anlamına gelmesi durumunda, muhtemelen burada niş vakalardan bahsediyoruz (örneğin, normalden çok daha uzun bir süre boyunca çalışan bir yöntem).
stakx - artık

5

Genel olarak null değerine ayarlanmasına gerek yoktur. Ancak sınıfınızda bir Sıfırlama işlevinin olduğunu varsayalım.

Öyleyse, Dispose bazı doğru düzgün uygulanamayabilir ve System.ObjectDisposed istisna atmak olabilir çünkü iki kez dispose çağırmak istemiyorsanız yapabilirsiniz.

private void Reset()
{
    if(_dataset != null)
    {
       _dataset.Dispose();
       _dataset = null;
    }
    //..More such member variables like oracle connection etc. _oraConnection
 }

Bunu ayrı bir bayrakla izlemek en iyisidir.
Thulani Chivandikwa

3

bu tür "kullanımdan sonra nesneleri null değerine ayarlamaya gerek yoktur" tamamen doğru değildir. Değişkeni elden çıkardıktan sonra NULL etmeniz gereken zamanlar vardır.

Evet, işiniz bittiğinde DAİMA aramanızı .Dispose()veya .Close()sahip olduğunuz herhangi bir şeyi aramanız gerekir . Dosya tanıtıcıları, veritabanı bağlantıları veya tek kullanımlık nesneler olsun.

Bundan ayrı LazyLoad'ın çok pratik modeli.

Ben ve örneği Say ObjAarasında class A. Class Adenilen bir ortak özelliği vardır PropBve class B.

Dahili olarak PropBve özel değişkenini _Bnull olarak kullanır. Ne zaman PropB.Get()kullanılır, bu çekleri görmek için _PropBnull ve eğer bir örneğini gerekli kaynak açılır BINTO _PropB. Sonra geri döner _PropB.

Deneyimlerime göre, bu gerçekten yararlı bir numara.

Null değerinin girilmesi gerektiğinde, A'yı içeriğinin _PropBönceki değerlerinin alt Aöğesi olacağı şekilde sıfırlar veya değiştirirseniz , _PropBLazyLoad'un kod IF EĞER doğru değeri almak için sıfırlanabilmesi için Dispose AND null komutunu vermeniz gerekir. gerektirir.

Sadece yaparsanız _PropB.Dispose()ve kısa bir süre sonra LazyLoad'ın null kontrolünün başarılı olmasını beklerseniz, null olmaz ve eski verilere bakacaksınız. Aslında, Dispose()sadece emin olmak için boş bırakmalısınız .

Kesinlikle aksi olsaydı, ama şimdi bu davranışı sergileyen Dispose()bir _PropBve dışında Dispose (ve böylece neredeyse kapsam dışı) yapan bir fonksiyon dışında kod var , özel pervane hala boş değil, ve eski veriler hala orada.

Sonunda, elden çıkarılan özellik geçersiz olacaktır, ancak bu benim bakış açımdan deterministik değildi.

Ana neden, dbkk alludes olarak ana konteyner ( ObjAile PropB) örneğini _PropBkapsamına rağmen tutmaktır Dispose().


Manuel olarak null değerine ayarlamanın arayan için daha ölümcül bir hata anlamına geldiğini gösteren iyi bir örnek.
rulolar

1

Null referansların mantıklı olduğu bazı durumlar vardır. Örneğin, bir öncelik sırası gibi bir koleksiyon yazarken ve sözleşmenizle, istemci bunları kuyruktan kaldırdıktan sonra bu nesneleri istemci için canlı tutmamalısınız.

Ancak bu tür şeyler sadece uzun ömürlü koleksiyonlarda önemlidir. Kuyruk, yaratıldığı işlevin sonunda hayatta kalmayacaksa, çok daha az önemlidir.

Bir bütün olarak, gerçekten rahatsız etmemelisiniz. Derleyici ve GC işlerini yapsın, böylece sizinkini yapabilirsiniz.



1

Stephen Cleary bu yazıda çok iyi açıklıyor: Çöp Toplamalarına Yardımcı Olmak için Değişkenleri Boş Olarak Ayarlamalı mıyım?

Diyor:

Sabırsız için Kısa Yanıt Evet, değişken statik bir alansa veya numaralandırılabilir bir yöntem (geri dönüş verimi kullanarak) veya eşzamansız bir yöntem (zaman uyumsuz ve beklemede kullanarak) yazıyorsanız. Aksi takdirde hayır.

Bu, normal yöntemlerde (numaralandırılamayan ve eşzamansız olmayan) yerel değişkenleri, yöntem parametrelerini veya örnek alanlarını null değerine ayarlamamanız anlamına gelir.

(IDisposable.Dispose uygulasanız bile, değişkenleri null olarak ayarlamamalısınız).

Dikkate almamız gereken önemli nokta Statik Alanlardır .

Statik alanlar her zaman kök nesnelerdir , bu nedenle çöp toplayıcı tarafından her zaman "canlı" olarak kabul edilirler . Statik alan artık gerekli olmayan bir nesneye başvuruyorsa, çöp toplayıcının onu toplama için uygun olarak kabul etmesi için null olarak ayarlanması gerekir.

Tüm işlem kapanıyorsa, statik alanları null değerine ayarlamak anlamsızdır. Öbek, tüm kök nesneler de dahil olmak üzere, o noktada toplanan çöp olmak üzere.

Sonuç:

Statik alanlar ; bu kadar. Başka her şey zaman kaybıdır .


0

GC uygulayıcılarının tasarımıyla, GC'yi nullification ile hızlandıramayacağınıza inanıyorum . Onların nasıl / GC çalıştırdığında ile kendinizi endişe tercih ediyorum eminim - Bu her yerde böyle bir muamele onu Varlık ve sizin için ... (yay baş aşağı, gökyüzüne yumruk yükseltir) korunması ve kollama .. .

Şahsen, değişkenleri kendiliğinden bir belge olarak tamamladığımda değişkenleri açıkça null olarak ayarladım. Beyan etmiyorum, kullanıyorum, sonra null değerine ayarlıyorum - artık ihtiyaç duyulmadığında hemen null. Diyorum ki, açıkça, "Resmi olarak seninle işim bitti ... git ..."

GC'd dilinde geçersizleştirme gerekli midir? Hayır. GC için faydalı mı? Belki evet, belki hayır, kesin olarak bilmiyorum, tasarımla gerçekten kontrol edemiyorum ve bu sürümle bugünün cevabı veya gelecekteki GC uygulamaları, cevabımı kontrolümün ötesinde değiştirebilir. Ayrıca nulling optimize edildiğinde / optimize edildiğinde, süslü bir yorumdan biraz daha fazlası olacaktır.

Adımlarımı izleyen bir sonraki fakir aptal için niyetimi netleştiriyorsa ve bazen GC'ye " yardımcı " olabilirse , o zaman bana değer. Çoğunlukla düzenli ve temiz hissetmemi sağlıyor ve Moğol düzenli ve temiz hissetmekten hoşlanıyor. :)

Buna şöyle bakıyorum: Programlama dilleri, insanların diğer insanlara niyet ve derleyiciye ne yapmaları için bir iş isteği vermesine izin vermek için var - derleyici bu isteği bir CPU için farklı bir dile (bazen birkaç) dönüştürüyor - CPU (lar), kullandığınız dili, sekme ayarlarınızı, yorumlarınızı, stil vurgularınızı, değişken adlarınızı vb. bir yuhaya verebilir. Kodda yazılan pek çok şey, bizim belirlediğimiz sıradaki CPU tarafından tüketilene dönüşmez. Bizim C, C ++, C #, Lisp, Babel, montajcı ya da gerçeklikten ziyade teori ne ise, bir çalışma ifadesi olarak yazılmıştır. Gördüğünüz şey elde ettiğiniz şey değil, evet, montajcı dilinde bile.

"Gereksiz şeyler" zihniyetini (boş satırlar gibi) "gürültü ve karmaşa kodundan başka bir şey olmadığını anlıyorum." Kariyerimin başlarında bendim; Tamamen anladım. Bu noktada kodu daha net yapan şeye doğru eğildim. Programlarıma 50 satır "gürültü" bile ekliyorum gibi değil - burada veya orada birkaç satır var.

Herhangi bir kuralın istisnaları vardır. Uçucu bellek, statik bellek, yarış koşulları, tek tonlar, "bayat" verilerin kullanımı ve tüm bu tür çürüklük senaryolarında bu farklıdır: bu, kendi hafızanızı yönetmeniz GEREKİR; GC'd Evren - umarım herkes bunu anlar. GC'd dilleri ile geri kalan zamanlar, gereklilik ya da garantili bir performans artışı yerine bir stil meselesidir.

Günün sonunda, GC için neyin uygun olup neyin olmadığını anladığınızdan emin olun; uygun şekilde kilitleyin, atın ve geçersiz kılın; balmumu açık, balmumu kapalı; nefes al nefes ver; ve diğer her şey için söylüyorum: Eğer iyi geliyorsa, yap. Kilometreniz değişebilir ... olması gerektiği gibi ...


0

Sanırım bir şeyi null değerine geri getirmek dağınık. Şimdiye kadar ayarlanan öğenin özellik yoluyla açıklandığı bir senaryo düşünün. Şimdi bir şekilde kod parçası madde atıldıktan sonra yanlışlıkla bu özelliği kullanır olduğunu tam olarak ne olduğunu anlamak için bazı araştırma gerektiren bir boş başvuru istisna alırsınız.

Çerçeve tek kullanımlık daha anlamlı olan ObjectDisposedException atmak izin verir inanıyorum. Bunları null değerine ayarlamamak bu nedenle daha iyi olur.


-1

Bazı nesneler .dispose(), kaynağı bellekten çıkarmaya zorlayan yöntemi varsayar .


11
Hayır değil; Dispose () yapar değil toplamak nesne - genellikle yönetilmeyen kaynakları serbest, deterministik temiz almanın gerçekleştirilmesi için kullanılır.
Marc Gravell

1
Determinizm yalnızca yönetilen kaynaklar değil, yönetilmeyen olanlar (yani bellek) için de geçerli olduğunu akılda tutarak
nicodemus13
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.