2017-05-16 79 views
0

我使用下面的程序來枚舉xml輸入中的元素。當到達最後一個元素時 x.MoveNext() 返回false,這部分是清楚的。我的問題是:IEnumerator <XElement>行爲

  • 爲什麼它繼續打印<c>即使列表耗盡
  • 爲什麼x.Current永遠等於空?這就是我所期待的,因爲XElement是引用類型

    static void Main(string[] args) 
    { 
        var x = GetXml(); 
        while (true) 
        { 
         var isMoving = x.MoveNext(); 
         Console.WriteLine($"Cursor moved: {isMoving}"); 
         Console.WriteLine($"Current element is null: {x.Current == null}"); 
         Console.WriteLine($"Current element: {x.Current}"); 
        } 
    } 
    
    static IEnumerator<XElement> GetXml() 
    { 
        return XElement.Parse(@"<x> 
               <a></a> 
               <b></b> 
               <c></c> 
              </x>").Elements().GetEnumerator(); 
    } 
    

輸出:

Cursor moved: true 
    Current element is null: False 
    Current element: <a></a> 
    Cursor moved: true 
    Current element is null: False 
    Current element: <b></b> 
    Cursor moved: true 
    Current element is null: False 
    Current element: <c></c> 
    Cursor moved: False     <- confusing bit 
    Current element is null: False 
    Current element: <c></c> 
+0

爲了將來的參考,這裏標籤的目的是引起人們對你的問題的關注。只有監控「xelement」和「ienumerator」標籤的人才會看到您的問題 - 幾乎沒有人。我在原始的新問題清單中偶然看到了這個問題,但是有太多的問題出現,因此任何人都不會注意到任何特定問題。如果你給C#添加了標籤(我冒昧),那麼你就可以在可能回答它的人面前獲得它。我意識到這似乎有點違反直覺,但這是網站最終被使用的方式。 –

+0

感謝您的建議Ed。我會忍受它。我很幸運,你偶然發現我的問題,因爲你的回答是完美的:) – Pasato

回答

1

爲什麼x.Current永遠等於空?

簡短的回答:布賴恩Kernighan的說,「你的編譯器是對語言的最終權威」 。它這樣做是因爲微軟的這些人以這種方式編寫了代碼。正如我們將在下面看到的,在這種情況下,這是由實施者決定的。用傳說中的計算機科學家Ray Dorset的話說,「只要做你自己感覺的事情」。

但這裏還是有一些有趣的地方。 According to MSDN

如果上次調用MoveNext返回false,則Current未定義。

這是通用的IEnumerator<T>,你正在使用的。這是你的問題的答案。

在某些語言中,例如JavaScript,「undefined」是一個關鍵字。在其他情況下,這是一個純英文單詞,用於描述規範留下的內容。如果他們說C或C#中的行爲是「未定義的」,則表示它取決於實施者。

以上不同從nongenericIEnumerator的記錄的行爲:

如果對MoveNext的最後一次調用返回false,調用當前拋出異常。

所有它告訴你的是,與一般的IEnumerator<T>,後MoveNext()返回false,Current可以做任何實施者感覺就像使它做。它可能會返回default(T)(這就是我所要做的),但它可能會返回最後一個項目。我會毫不猶豫地讓它拋出一個異常,因爲我們已經有不止一個的Microsoft .NET代碼(你發現的那個,以及你將要見到的幾個代碼)不會拋出,而且MSDN並沒有說它可能,所以採取這個意思,它只是沒有。

I tested some other cases

public class Program 
{ 
    public static void Main() 
    { 
     //var x = GetEnumeratorNonGeneric(1); 
     //var x = GetEnumerator(1); 
     //var x = GetEnumeratorInt(1); 
     //var x = GetEnumeratorIntRangeSelect(1); 
     var x = GetEnumeratorIntList(1); 

     for (int i = 0; i < 2; ++i) 
     { 
      var isMoving = x.MoveNext(); 
      Console.WriteLine($"Cursor moved: {isMoving}"); 
      Console.WriteLine($"Current element is null: {x.Current == null}"); 
      Console.WriteLine($"Current element: {x.Current}"); 
     } 
    } 

    static IEnumerator<String> GetEnumerator(int count) 
    { 
     return Enumerable.Range(1, count).Select(n => n.ToString()).GetEnumerator(); 
    } 

    static IEnumerator<int> GetEnumeratorInt(int count) 
    { 
     return Enumerable.Range(1, count).GetEnumerator(); 
    } 

    static IEnumerator<int> GetEnumeratorIntRangeSelect(int count) 
    { 
     return Enumerable.Range(1, count).Select(n => n).GetEnumerator(); 
    } 

    static IEnumerator<int> GetEnumeratorIntList(int count) 
    { 
     return Enumerable.Range(1, count).ToList().GetEnumerator(); 
    } 

    static IEnumerator GetEnumeratorNonGeneric(int count) 
    { 

     return new ArrayList(Enumerable.Range(1, count).Select(n => n.ToString()).ToArray()).GetEnumerator(); 
    } 
} 

輸出爲GetEnumeratorNonGeneric():從GetEnumerator()

Cursor moved: True 
Current element is null: False 
Current element: 1 
Cursor moved: False 
Run-time exception (line -1): Enumeration already finished. 

Stack Trace: 

[System.InvalidOperationException: Enumeration already finished.] 

輸出。這不會拋出,但在這種情況下確實返回default(T),null

Cursor moved: True 
Current element is null: False 
Current element: 1 
Cursor moved: False 
Current element is null: True 
Current element: 

GetEnumeratorInt()輸出。 Enumerable.Range()返回使用的最後一個值Current枚舉:

Cursor moved: True 
Current element is null: False 
Current element: 1 
Cursor moved: False 
Current element is null: False 
Current element: 1 

隨着GetEnumeratorIntRangeSelect(),我們發現Select()獲得我們的枚舉,其Current回報default(T)MoveNext()返回false。這與上面的GetEnumerator()真的是多餘的,但它說明了值類型而不是引用類型會發生什麼。

Cursor moved: True 
Current element is null: False 
Current element: 1 
Cursor moved: False 
Current element is null: False 
Current element: 0 

List<T>枚舉我們從調用ToList()獲得也Current結束後返回default(T)

我沒有在結束後發現在Current上拋出的通用IEnumerator<T>,但正如您所看到的,我沒有找到一個好項目來尋找它。


或者類似的話;我找不到確切的措辭。

可能不是真正的計算機科學家。