2010-07-13 90 views
9

在循環之外的循環中聲明一個變量,而不是在內部聲明是更好的嗎?有時候我會看到一些變量在循環中聲明的例子。這是否有效地導致程序在每次循環運行時爲新變量分配內存?或者,.NET足夠聰明,知道它實際上是同一個變量。例如this answer變量聲明是否應該放在循環之外?

public static void CopyStream(Stream input, Stream output) 
{ 
    byte[] buffer = new byte[32768]; 
    while (true) 
    { 
     int read = input.Read (buffer, 0, buffer.Length); 
     if (read <= 0) 
      return; 
     output.Write (buffer, 0, read); 
    } 
} 

這個修改後的版本會更有效嗎?

public static void CopyStream(Stream input, Stream output) 
{ 
    int read; //OUTSIDE LOOP 
    byte[] buffer = new byte[32768]; 
    while (true) 
    { 
     read = input.Read (buffer, 0, buffer.Length); 
     if (read <= 0) 
      return; 
     output.Write (buffer, 0, read); 
    } 
} 

回答

9

不,它不會更有效率。不過,我把它改寫這樣恰好反正聲明它的外循環:

byte[] buffer = new byte[32768]; 
int read; 
while ((read = input.Read(buffer, 0, buffer.Length)) > 0) 
{ 
    output.Write(buffer, 0, read); 
} 

我不是一般使用條件下的副作用的粉絲,但有效的方法Read是給你們兩個數據位:您是否已達到流的末尾,以及您已閱讀了多少內容。 while循環現在說,「雖然我們已經設法讀取一些數據......複製它。」

這是一個有點像使用int.TryParse

if (int.TryParse(text, out value)) 
{ 
    // Use value 
} 

同樣,你正在使用調用條件方法的副作用。正如我所說,當你處理返回兩位數據的方法時,我不會爲這種特定模式做以外的習慣,除了

同樣的事情出現從TextReader讀線:

string line; 
while ((line = reader.ReadLine()) != null) 
{ 
    ... 
} 

要回到你原來的問題:如果一個變量是要在一個循環的每次迭代進行初始化它只是用來在循環體內,我幾乎總是在循環中聲明它。這裏是一個小的例外是如果變量是由匿名函數捕獲 - 在這一點上它會讓行爲上的差異,我會選擇哪種形式給了我所期望的行爲......但是這幾乎總是「內部申報「無論如何。

編輯:當涉及到範圍界定,上面的代碼確實離開變量在更大範圍比它需要......但我相信它使循環更加清晰。如果你願意,你可以隨時通過引入新的範圍解決這個問題:

{ 
    int read; 
    while (...) 
    { 
    } 
} 
+1

我有一個範圍的問題達成一致。當有人不得不閱讀舊代碼(或其他人的代碼)時,最好有適當範圍的變量。當我離開範圍時,我不再擔心該變量的最後一個值是什麼。 – Torlack 2010-07-13 21:21:43

+0

@Torlack:我會編輯來解決這個問題。 – 2010-07-13 21:23:30

1

我通常優選後者爲個人習慣問題,因爲即使.NET是足夠聰明,其他環境中,我稍後可能工作可能不夠聰明。它可能只不過是編譯成循環內的一行額外的代碼來重新初始化變量,但它仍然是開銷。

即使他們在任何給定的例子所有可測量的目的是相同的,我會說後者少造成的長遠問題的機會。

+2

我不同意 - 因爲你現在得到了一個比它需要的範圍更大的變量,這對可讀性通常是不利的。就我個人而言,我認爲你應該適應你工作環境的習慣用語 - 如果你嘗試以同樣的方式編寫C++,Java和C#,最終會遇到比這更大的問題。 – 2010-07-13 21:08:36

+0

夠公平了,我絕對同意應該正確使用這些工具,而不是試圖強迫它們看起來相似。我當然不想暗示其他情況。我想在這個特定的例子中,範圍並不是什麼大問題,因爲無論如何它會立即結束。對於代碼整體來說,肯定有很多需要說的,因爲對於類似問題的所有情況,很少有全局解決方案。 – David 2010-07-13 21:19:42

3

在不可能的環境,不幫你這個問題,它仍然是一個微型優化。諸如清晰度和適當範圍確定等因素比邊緣情況要重要得多,因爲這種情況可能幾乎沒有什麼區別。

你應該給你的變量適當的範圍,而不考慮性能。當然,複雜的初始化是一個不同的野獸,所以如果某個東西只能初始化一次,但只能在一個循環中使用,你仍然想在外面聲明它。

+0

+1即使在循環的每次迭代中都分配了變量,您可能也想在裏面聲明它。性能差異大部分時間可以忽略不計,但範圍確定不是。 – helpermethod 2010-07-13 22:02:01

+0

我同意,我主要談論的是在循環內部使用但未更改的變量,但是在循環之外聲明並初始化,因爲對象的初始化並不重要。正如Jon Skeet在他的回答中提到的那樣,你可以引入一個新的範圍來保持它在循環之外,但仍然適當的範圍。這對於像資源句柄這樣的東西非常適用,在這種情況下,您可以使用using(...)構造引入一個新範圍。 – 2010-07-13 22:23:31

2

我會同意大多數其他答案與警告。

如果您使用的是lambda表達式,您必須注意捕獲變量。

static void Main(string[] args) 
{ 
    var a = Enumerable.Range(1, 3); 
    var b = a.GetEnumerator(); 
    int x; 
    while(b.MoveNext()) 
    { 
     x = b.Current; 
     Task.Factory.StartNew(() => Console.WriteLine(x)); 
    } 
    Console.ReadLine(); 
} 

會給結果

3 
3 
3 

static void Main(string[] args) 
{ 
    var a = Enumerable.Range(1, 3); 
    var b = a.GetEnumerator(); 
    while(b.MoveNext()) 
    { 
     int x = b.Current; 
     Task.Factory.StartNew(() => Console.WriteLine(x)); 
    } 
    Console.ReadLine(); 
} 

會給結果

1 
2 
3 

或某種秩序存在的。這是因爲當任務最終啓動時,它將檢查它對x的引用的當前值。在第一個例子中,所有3個循環指向相同的引用,在第二個例子中它們都指向不同的引用。

2

正如許多像這樣的簡單優化的情況一樣,編譯器會爲您處理它。如果您嘗試了以上兩點,看看組件IL在反彙編,你可以看到,他們都聲明一個INT32讀變量,雖然它重新排序的聲明:

.locals init ([0] int32 read, 
      [1] uint8[] buffer, 
      [2] bool CS$4$0000) 

    .locals init ([0] uint8[] buffer, 
      [1] int32 read, 
      [2] bool CS$4$0000) 
相關問題