2011-04-09 76 views
-5

IM意識到Eric Lippert's blog post about situation ,但我認爲這是一個不同的情況,因爲該領域immutates本身而不是它的領域。如果Enumerator是隻讀的,你怎麼能解釋調用MoveNext()不會顯示任何效果,並且輸出始終爲0?自我變異只讀結構領域

class SomeClass 
    { 
     private List<int> list; 
     private [readonly] List<int>.Enumerator enumerator; 

     public SomeClass() 
     { 
      list = new List<int>() { 1, 2, 3 }; 
      enumerator = list.GetEnumerator(); 
     } 

     public int ReadValue() 
     { 
      if (enumerator.MoveNext()) 
       return enumerator.Current; 
      return -1; 

     } 
    } 
static void Main() 
    { 
     SomeClass c = new SomeClass(); 
     int value; 
     while ((value = c.ReadValue()) > -1) 
      MessageBox.Show(value.ToString()); 
    } 

回答

2

如果我沒有理解這個,你貼,其中的MoveNext()在副本上執行該語句

if(enumerator.MoveNext()) 

首先對枚舉的副本,埃裏克利珀的博客文章。該副本死亡,下一行:

return enumerator.Current; 

返回原來的枚舉,而不是副本,這是當前爲什麼你總是得到0

+0

還是我不能明白爲什麼NoveNext創建一個副本,我不是治療sturct作爲對象 – TakeMeAsAGuest 2011-04-09 10:05:16

+0

和仍然無法明白爲什麼刪除只讀使它按預期執行。順便說一下,如果保持列表的參考.Enumerator作爲枚舉(界面引用類型)其正常執行了。 – TakeMeAsAGuest 2011-04-09 10:15:58

+1

它似乎使這個副本的任何類型的引用,無論是結構或類。 :| – ohmusama 2011-04-09 11:15:07

2

只讀關鍵字給出了C#編譯器很難。它不知道MoveNext()和Current是否有違反只讀合約的副作用。 MoveNext()當然會。所以要生成有效的代碼,它必須必須創建一個迭代器值的副本。它發生兩次,一次用於MoveNext()方法調用,再次讀取Current屬性時。通過在程序上運行ildasm.exe,您可以輕鬆地看到該副本,該副本在Debug版本中被命名爲CS $ 0 $ 0001。

如果編譯器至少生成該代碼警告這將是很好。雖然很難做到,但確實需要知道會員是否有副作用。它不知道。有方式太多的結構類型與屬性獲取沒有副作用,所以才總是產生的警告是不可行的。

要讓它知道的功能的種類是const關鍵字,因爲它在C++中使用。一個方法可以被聲明爲const,以表明它不會改變對象的狀態。我認真地懷疑這個功能會把它變成C#語言,但是編寫const正確的代碼並不是那麼容易,坦率地說,它只是一個皮塔餅。

+0

感謝您的解釋,很少有人能回答這個問題。我想在c#語言中使用const關鍵字,編譯器可以檢查方法在平凡的情況下是否改變狀態,如果不能,它只會給出錯誤。這會給編譯器和jit優化器帶來巨大的機會來更好地優化代碼。我會很高興,如果你給我的其他問題你的想法:http://stackoverflow.com/questions/5600623/recursively-calling-method-for-object-reuse-purpose可以重複線程回答。我不能放棄你,因爲我的聲望低於15 – TakeMeAsAGuest 2011-04-09 14:01:20

+0

我們不能給你。將功能請求發佈到connect.microsoft.com – 2011-04-09 14:03:40

+0

我仍然認爲MoveNext不會改變結構,只是它的內部狀態,而不是地址。 – TakeMeAsAGuest 2011-04-09 14:05:36

10

我認爲這是一個不同的情況

你是不正確的。這正是我在我參考的博客文章中描述的情況。

在此重申我的分析:在結構每一個非靜態方法調用需要一個「裁判」參數被稱爲「本」。我們不在參數列表中顯示「ref this」參數,但它在那裏,由編譯器爲您生成。任何通過ref的東西必須是變量。由於readonly變量可能是(並且在這種情況下會被調用),所以我們必須確保readonly變量永遠不會被ref傳遞。在只讀結構上調用方法時,我們創建一個臨時變量,將結構複製到臨時變量,在方法調用中將ref傳遞給臨時變量「this」,然後丟棄該臨時變量。這解釋了你所看到的行爲; MoveNext引起的每一個突變都會發生在一個被丟棄的副本上。

你能解釋爲什麼這種狀況 - 這是完全一樣的一個我在我的博客形容 - 是有什麼不同?你認爲什麼是不同的調查員,使他們特別?

+1

我理解變異變量引用,而不是內容。在只讀對象obj的情況下,readonly關鍵字會保護引用變量的值,而不是對象本身。但現在我看到價值類型,它保護價值。我不明白爲什麼變異結構需要一個新的副本。以及爲什麼變異readonly變量不會拋出異常?非常感謝你對你的解釋 – TakeMeAsAGuest 2011-04-10 18:33:37

+0

@TakeMeAsAGuest:我不明白你所說的「變異變量的引用,而不是內容」的意思。一個變量有內容。如果變量是引用類型,則該內容是引用;如果變量是值類型,則該內容是引用。這就是爲什麼引用類型和值類型被稱爲「引用」類型和「值」類型的原因。突變只讀變量不會拋出異常,因爲突變只讀變量是不可能的。在編譯時將參考類型的只讀變量變爲非法,並且對值類型的只讀變量進行變異來變更副本。 – 2011-04-10 22:14:18

+0

我想知道這個決定背後的動機。並且你是否也複製了「參考變量的內容」? – TakeMeAsAGuest 2011-04-11 07:02:01