2008-08-05 56 views
170

一旦完成對象的設置,是否應將所有對象都設置爲null(在VB.NET中爲Nothing)?在.NET中使用後將對象設置爲Null/Nothing

我明白,在.NET中,處理實現接口的任何對象實例都是必不可少的,以釋放一些資源,儘管該對象在處置後仍可能是某種東西(因此在表單中屬於isDisposed屬性),所以我認爲它仍然可以駐留在內存中,或者至少部分存在?

我也知道,當一個對象超出作用域時,它會被標記爲垃圾收集器的下一個通道的收集準備好(儘管這可能需要時間)。

因此,考慮到這一點,將它設置爲null加速系統釋放內存,因爲它不必知道它不再在範圍內,並且它們有任何不良副作用?

MSDN文章從來沒有這樣做的例子,目前我這樣做,因爲我不能 看到傷害。不過,我遇到了各種意見,所以任何意見都是有用的。

+3

+1很好的問題。有沒有人知道編譯器將完全優化分配的情況?即有人在不同情況下查看MSIL,並注意到IL將對象設置爲空(或缺少)。 – 2010-09-27 07:43:55

回答

66

卡爾絕對正確,使用後無需將對象設置爲空。如果某個對象實現了IDisposable,請確保在完成該對象(包裝在try .. finallyusing()塊中)後致電IDisposable.Dispose()。但即使您不記得致電Dispose(),對象上的終結方法應該會爲您調用Dispose()

我認爲這是一個良好的治療:

Digging into IDisposable

Understanding IDisposable

沒有任何一點在試圖第二猜測GC和其管理戰略,因爲它是自我調節和不透明的。有關於與傑弗裏裏希特在這裏點網岩石的內部運作商量好了:Jeffrey Richter on the Windows Memory Model和 Richters書CLR via C#第20章有很大的治療:

+5

關於不設置爲空的規則不是「硬性和快速」...如果對象被放在大對象堆上(大小大於85K),那麼如果您將對象設置爲null,它將有助於GC使用它完成。 – 2008-11-01 03:46:16

+0

我同意的程度有限,但除非您開始感受到內存壓力,否則我認爲不需要在使用後將對象設置爲null以提前優化。 – Kev 2008-11-01 20:14:36

+18

「不要過早優化」的整個業務聽起來更像「首選慢,不要擔心,因爲CPU速度越來越快,CRUD應用程序不需要速度。」它可能只是我而已。 :) – BobbyShaftoe 2008-12-20 04:42:09

-1

某些對象假設.dispose()強制資源從內存中刪除的方法。

+10

不,它不; Dispose()不*收集對象 - 它用於執行確定性清理,通常釋放非託管資源。 – 2008-11-02 11:08:01

+1

記住確定性只適用於受管理的資源,而不適用於非受管理的資源(即內存) – nicodemus13 2011-04-05 17:05:49

4

您應該將變量設置爲null的唯一時間是變量未超出範圍且不再需要與其關聯的數據。否則就沒有必要。

+2

這是事實,但這也意味着您應該重構代碼。我不認爲我曾經需要聲明一個超出預期範圍的變量。 – 2008-08-05 20:36:31

+1

如果「變量」被理解爲包含對象字段,那麼這個答案就很有意義。在「變量」僅表示「方法」的「局部變量」的情況下,那麼我們可能在這裏談論利基案例(例如,運行時間比平常長得多的方法)。 – stakx 2017-05-28 12:21:42

7

另外:

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

有一些情況下是有意義的空引用。例如,當你寫一個集合 - 比如一個優先級隊列 - 和你的契約時,你不應該在客戶端將它們從隊列中刪除之後讓這些對象保持活動狀態。

但這種事情只在長壽的集合中很重要。如果隊列在它創建的函數結束時不能存活下來,那麼它的重要性就會降低很多。

總的來說,你真的不應該打擾。讓編譯器和GC做他們的工作,這樣你就可以做你的工作。

8

在一般情況下,沒有必要爲null使用後的對象,但在某些我發現這是一個很好的做法。

如果一個對象實現了IDisposable並存儲在一個字段中,我認爲它很好,以避免使用處置對象。以下排序的錯誤可能是痛苦的:

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

這是很好的空場配置之後,並就在那裏現場再次使用的線得到NullPtrEx。否則,你可能會遇到一些神祕的bug(具體取決於DoSomething的作用)。

6

如果您感覺需要變量,您的代碼結構不夠緊密。

有許多的方式來限制變量的範圍:

正如史蒂夫Tranby提到

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

同樣,你可以簡單地使用大括號:

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

我發現使用沒有任何「標題」的大括號來真正清理代碼並且使其更容易理解。

33

當您完成對象設置時,避免將對象設置爲null的另一個原因是,它可以確保它們保持更長時間的活動狀態。

例如

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

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

將允許通過SOMETYPE稱爲所述對象到所述呼叫到「DoSomething的」,但

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; 
} 

有時可以保持對象的生命,直到所述方法的結束後GC'd。 JIT will usually optimized away the assignment to null,所以兩個代碼的結尾都是一樣的。

1

採取在這篇文章一看還有:http://www.codeproject.com/KB/cs/idisposable.aspx

在大多數情況下,一個對象設置爲NULL,則沒有效果。你唯一應該確定的是,如果你正在處理一個大於84K大小的「大對象」(比如位圖)。

3

這種「沒有必要使用後將對象設置爲null」並不完全準確。有時你需要在處理完變量後清空它。

是的,你應該總是打電話.Dispose().Close()任何事情,當你完成。無論是文件句柄,數據庫連接還是一次性對象。

與此不同的是LazyLoad非常實用的模式。

假設我已經實例化了ObjAclass AClass A有一個名爲PropBclass B的公共財產。

在內部,PropB使用私有變量_B,默認爲null。當使用PropB.Get()時,它檢查_PropB是否爲空,如果是,則打開將B實例化爲_PropB所需的資源。然後它返回_PropB

以我的經驗,這是一個非常有用的技巧。

這裏需要空進來是,如果你重新設置或以某種方式的_PropB的含量的A先前值的孩子改變,你將需要處理和空出_PropB所以LazyLoad可以重置如果代碼需要,可以獲取正確的值。

如果您只做_PropB.Dispose(),並且在期望LazyLoad的空檢查成功之後不久,它將不會爲空,並且您將查看陳舊的數據。實際上,您必須在Dispose()之後將其清零。

我確定希望它是其他方式,但我現在有代碼現在展示此行爲後_PropBDispose()和執行Dispose(因此幾乎超出範圍)的調用函數之外,私人道具仍然不爲空,陳舊的數據仍然存在。

最終,被處置的財產將被清空,但從我的角度來看,這是非確定性的。

核心原因,亞庇市政廳暗指是父容器(ObjAPropB)是保持在範圍_PropB實例,儘管Dispose()

5

一般不需要設置爲空。但是,假設你在課堂上有重置功能。

那麼你可能會這樣做,因爲你不想調用兩次配置,因爲一些Dispose可能沒有正確實現並拋出System.ObjectDisposed異常。

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

我相信通過GC實現者的設計,你不能加快 GC與無效。我相信他們更喜歡你不用擔心自己在GC運行時如何/如何對待它 - 正在保護和注視着你......(低頭向下,向天空揮拳) ...

就我個人而言,當我將它們作爲自我文檔形式完成時,我經常將變量明確地設置爲null。我沒有聲明,使用,然後設置爲null - 在不再需要它們之後立即刪除它。我明確地說,「我已與你正式完成......不見了......」

在GC'd語言中是否必須是無效的?不是。它對GC有幫助嗎?也許是的,也許不是,一定不知道,在設計上我真的無法控制它,不管今天的答案是這個版本還是那個,未來的GC實現可能會改變超出我的控制範圍的答案。另外如果/當零點優化出來,它只是一個看中評論如果你願意。

我想,如果它讓我的意圖更清晰,那麼下一個跟隨我的腳步的可憐的傻瓜,如果它有時可能幫助GC,那麼這對我來說是值得的。大多數情況下,這讓我感覺整潔清晰,蒙戈喜歡整潔清晰。 :)

我看起來就像這樣:編程語言的存在是爲了讓人們給他人一個意圖的想法和一個編譯器的工作請求 - 編譯器將該請求轉換成不同的語言(有時是幾個)--CPU(s)可以提供你使用的語言,你的標籤設置,註釋,風格強調,變量名等等 - CPU的所有關於比特流的信息,告訴它哪些寄存器和操作碼和內存位置twiddle。用代碼編寫的許多東西不會按照我們指定的順序轉換成CPU所消耗的東西。我們的C,C++,C#,Lisp,Babel,彙編程序或任何其他理論而不是現實,都是作爲工作陳述寫成的。你看到的不是你得到的,是的,即使是彙編語言。

我明白「不必要的東西」(如空白行)的思維方式「只是噪音和混亂的代碼。」那是我職業生涯的早些時候;我完全明白這一點。在這個時候,我傾向於使代碼更清晰。這不像我在我的程序中添加50行「噪音」 - 這裏或那裏只有幾行。

任何規則都有例外。在具有易失性存儲器,靜態存儲器,競態條件,單例,使用「陳舊」數據以及所有類型的腐敗的情況下,這是不同的:你需要管理自己的內存,鎖定和取消爲apropos,因爲內存不是GC'd宇宙 - 希望每個人都明白這一點。其餘時間使用GC語言,這是一個風格問題,而不是必要的問題,或者保證性能提升。

在一天結束時,請確保您瞭解什麼是符合GC的條件,哪些不符合條件;鎖定,處置和適當的廢除;打蠟,打蠟;呼吸,呼吸;對於我說的所有其他事情:如果感覺很好,那就去做吧。你的里程可能會有所不同,因爲它應該...

相關問題