2009-07-27 57 views
3

在C#中,重新初始化一個先前聲明的變量而不是聲明和初始化一個新的變量會有好處還是缺點? (忽略的簡潔和易讀性的想法)重新初始化變量或聲明新的?

例如,比較這兩個樣本:

DataColumn col = new DataColumn(); 
col.ColumnName = "Subsite"; 
dataTable.Columns.Add(col); 

col = new DataColumn(); // Re-use the "col" declaration. 
col.ColumnName = "Library"; 
dataTable.Columns.Add(col); 

VS

DataColumn col1 = new DataColumn(); 
col1.ColumnName = "Subsite"; 
gridDataTable.Columns.Add(col1); 

DataColumn col2 = new DataColumn(); // Declare a new variable instead. 
col2.ColumnName = "Library"; 
gridDataTable.Columns.Add(col2); 

一個類似的例子,涉及循環:

string str; 
for (int i = 0; i < 100; i++) 
{ 
    str = "This is string #" + i.ToString(); // Re-initialize the "str" variable. 
    Console.WriteLine(str); 
} 

vs

for (int i = 0; i < 100; i++) 
{ 
    string str = "This is string #" + i.ToString(); // Declare a new "str" each iteration. 
    Console.WriteLine(str); 
} 

編輯:謝謝大家迄今爲止的答案。閱讀完後,我想我會擴展一下我的問題:

請糾正我,如果我錯了。

當我聲明並初始化一個像System.String這樣的引用類型時,我有一個指向該對象(存在於堆棧上)和該對象的內容(存在於堆中的指針)(只能通過指針訪問)。

在第一個循環示例中,似乎我們只創建一個指針「str」,並且我們創建了100個String類的實例,每個實例都存在於堆中。在我看來,當我們遍歷循環時,我們只是每次更改「str」指針以指向String類的新實例。那些不再有指向他們的指針的「舊」字符串將被垃圾收集 - 儘管我不確定何時會發生這種情況。

在第二個循環示例中,除了創建100個String類實例外,似乎我們還創建了100個指針。

雖然我不確定堆棧上不再需要的物品會發生什麼。我不認爲垃圾收集器也擺脫了這些物品;也許他們一旦退出範圍就立即從堆棧中移除?即使這是真的,我認爲只創建一個指針並更新它指向的內容比創建100個不同的指針更有效,每個指針指向一個唯一的實例。

我明白「過早優化是邪惡的」論點,但我只是試圖獲得對事物的更深入的理解,而不是將我的程序優化爲死亡。

+0

「忽略對人的可讀性的想法」 - 忽略第二個最重要的因素(第一個是「正確性」)可能是做出決定的一個壞方法。 – 2009-07-27 17:35:43

+0

@Eric:我添加了這句話只是爲了勸阻像「這樣做,它更可讀」的答案。當然,我認爲使代碼可讀性至關重要。我只是在尋找更多技術性的答案,就好像是有性能差異一樣。 – 2009-07-27 18:02:03

回答

6

你的第二個例子有一個更清晰的答案,第二個例子是更好的答案。之所以這樣,是因爲變量str只用於for塊中。在for塊之外聲明變量意味着另一段代碼可能錯誤地綁定到這個變量,從而導致應用程序出現錯誤。您應該在可能的最具體範圍內聲明所有變量以防止意外使用。

對於第一個樣本,我認爲這更多是一個偏好問題。對我而言,我選擇創建一個新變量,因爲我相信每個變量都應該有一個目的。如果我重複使用變量,那通常表明我需要重構我的方法。

0

如何使用單詞「using」在花括號結束後破壞對象本身? 我不確定,但這就是我的想法。我也想知道你的意見。

對於第二個例子,我總是使用第二個例子,但我不太清楚,但是例如在ACM-ICPC競賽中,由於忘記重新初始化而失去了錯誤,我用這種方式。

+1

這隻適用於實現IDiposable的類,而不是字符串或DataColumn。 – 2009-07-27 17:03:54

1

這聽起來像一個旨在提供過早優化信息的問題。我懷疑任何情況在99.9%的軟件中都有什麼不同。內存正在被創建和使用。唯一的區別是變量引用。

要確定是否有益處或缺點,您需要確實關心組件大小或性能的情況。如果你不能滿足尺寸要求,那麼測量兩種選擇之間的裝配尺寸差異(儘管你更有可能在其他領域獲得收益)。如果您無法達到性能要求,請使用分析器查看代碼的哪一部分工作速度過慢。

1

它主要是一個可讀性問題,無論您使用相同的聲明名稱或不是無關緊要,因爲無論您是創建兩個單獨的對象。你真的應該創造一個單一的焦點對象或變量,它會讓你的生活更輕鬆。

至於你的第二個例子,初始化的唯一真正區別是通過將你的字符串放在「for」循環的範圍之外,你將它暴露在更多的外部影響之下,這有時可能是有用的。在循環內部或外部聲明它沒有內存或速度優勢。請記住,無論何時您對字符串變量進行更改,您都會創建一個新字符串。因此,舉例來說:

string test = "new string"; 
test = "and now I am reusing the string"; 

是與創建兩個單獨字符串,如:

string test1 = "new string"; 
string test2 = "and now I am reusing the string"; 

要解決這個問題,你可以使用StringBuilder類,它允許你修改字符串而不創建一個新的字符串,並應該在字符串被大量修改的情況下使用,特別是在循環內部。

0

大多數情況下,不應該沒有區別。主要區別在於局部變量(在類的情況下,它們的「指針」)被存儲在堆棧和第一種情況下,如果你的函數是由於某種原因遞歸的,有兩個局部變量而不是一個會導致你在深度遞歸函數中更快地耗盡堆棧空間。在這兩種情況下接近這個限制都是一個跡象,你應該使用非遞歸方法。

而且,只提它,你可以完全跳過變量和寫:

dataTable.Columns.Add(new DataColumn() { ColumnName = "Subsite" }); 
dataTable.Columns.Add(new DataColumn() { ColumnName = "Library" }); 

我相信這性能代價就會像有2個局部變量,但我可能是錯在那裏。我不記得在IL代碼中究竟產生了什麼。

+0

局部變量不必存儲在堆棧上。如果這樣做確實安全,則允許抖動註冊當地人。抖動和C#編譯器也可以爲不同的變量重新使用堆棧槽,如果這樣做可以證明是安全的話。 – 2009-07-27 17:38:39