2010-11-16 47 views
2

這個問題背誦了另一個question,我提到了通過在遍歷它時通過修改對象來濫用IEnumerable接口。當類沒有實現IEnumerable時,GetEnumerator方法仍然是冪等的

普遍的共識是沒有任何實現IEnumerable的東西應該是冪等的。但.net支持編譯時使用foreach語句進行鴨子打字。任何提供IEnumerator GetEnumerator()方法的對象都可以在foreach語句中使用。

那麼,GetEnumerator方法應該是冪等的還是它實現IEnumerable的時候呢?

EDIT(由上下文)

把一些情況下這一輪我的建議是,遍歷隊列時,因爲它不用每個項目出列。此外,在調用GetEnumerator之後,任何推入隊列的新對象都將被迭代。

+1

我不會將GetEnumerator添加到Queue本身,而是定義一個函數'IEnumerable DequeueOnEnumeration()',它可以像'foreach(T中的元素,在queue.DequeueOnEnumeration())'中一樣使用。這樣的語義*更清晰。 – CodesInChaos 2010-11-27 19:12:44

回答

3

這不是類型這是冪等的 - 這甚至沒有多少意義;你可能意味着不可改變,但這並不明確。這是GetEnumerator方法本身,它通常是冪等的。

雖然我會說這是通常是的情況下,我可以設想一些特殊情況下,有一個非冪等的GetEnumerator方法是有意義的。例如,它可能是因爲您的數據只能被讀取一次(因爲它是從Web服務器流出的,不會再服務於相同的請求,或類似的東西)。在這種情況下,GetEnumerator將不得不有效地使數據源無效,以便將來的調用會引發異常。

這樣的類型和方法當然應該非常仔細地記錄下來,但我認爲它們是合理的。

+0

認爲你可能是一個可以回答的人,我看到了你對類似帖子的迴應,並認爲你可能有一個不是一條硬性規定的意見。不知道你是否讀過關於阻塞隊列的其他問題,我覺得這與你的流示例類似。嚴格地說,你不應該能夠查看隊列的內容,並且使用foreach是將所有項目彈出的「一種方式」。當我說冪等性時,我澄清了我的意思,謝謝。 – Bronumski 2010-11-16 14:18:35

+0

您能澄清一下您是否認爲實現IEnumerable是否相關? – Bronumski 2010-11-16 14:36:08

3

這個討論是一箇舊的,據我所知沒有共同的共識。

請不要混淆(運行時)Duck-Typing的概念和濫用支持編譯器的編譯器foreach來支持您所需的語義。

您似乎混淆的另一個概念是冪等性與不可變性。根據你的措詞你試圖描述第二個,這意味着提供枚舉數的對象在枚舉過程中被修改。另一方面,冪等性意味着你的枚舉器在被調用兩次時會產生相同的結果。

現在我們已經明確了這一點,您需要仔細確定IEnumerable操作應支持的語義。某種枚舉是很難使冪等(即涉及緩存),也通常分爲以下類別之一:

  • 枚舉過隨機地改變 數據(即一個隨機數發生器,傳感器流)
  • 枚舉在共享狀態 (例如文件,數據庫,溪流等)

在另一方面,這隻佔「源」操作。如果您正在使用枚舉器實現篩選或轉換操作,則應始終嘗試使它們具有冪等性。

+0

我試圖從問題中刪除我的場景以保持問題的開放性。我在前面的問題中提出的建議是,當迭代一個阻塞隊列時,每個項目都會在隊列中出隊。此外,在調用GetEnumerator之後,任何推入隊列的新對象都將被迭代。 – Bronumski 2010-11-16 14:31:28

+0

您能澄清一下您是否認爲實現IEnumerable是否相關? – Bronumski 2010-11-16 14:36:25

0

看來你想要一個隊列類,你可以從一個漂亮的單行列表中出列所有的項目。

這個想法本身沒有錯;我只是質疑你的偏好,專門用GetEnumerator來實現你的目標。

爲什麼不簡單地編寫一個更明確的方法呢?例如,DequeueAll,或類似的東西。

例子:

// Just a simplistic example. Not the way I'd actually write it. 
class CustomQueue<T> : Queue<T> 
{ 
    public IEnumerable<T> DequeueAll() 
    { 
     while (Count > 0) 
     { 
      yield return Dequeue(); 
     } 
    } 
} 

(注意上面甚至是擴展方法,如果它代表從字面上你會超出想要的東西是由Queue<T>已經提供的只有功能。 )

通過這種方式,您仍然可以獲得「乾淨」的代碼,我懷疑您之後沒有非冪等的(潛在)混淆GetEnumerator

// Pretty clean, right? 
foreach (T item in queue.DequeueAll()) 
{ 
    Console.WriteLine(item); 
} 
+0

這幾乎是我來的。實際上有人指出.net 4引入了一個IProducerConsumerCollection接口和一個GetConsumingEnumerable方法的BlockingCollection實現。這個問題更多的是關於它是否是不好的做法,因爲該類有一個GetEnumerator方法,因爲該類實現了IEnumerable或者都沒有。 – Bronumski 2010-11-16 15:06:51

+0

順便說一句我喜歡擴展方法的想法。 – Bronumski 2010-11-16 15:35:50

0

我會建議在集合上使用ForEach不應該改變它,除非集合類型的名稱意味着會發生。我腦海中的問題是如果執行一個方法將某個集合消費到可枚舉的東西(例如,允許「MyThing.DequeueAsEnum中的For Each Foo」),應該返回的問題。如果DequeueAsEnum返回一個iEnumerable,那麼有人可能會期望脫離「Dim myIEnumerable As IEnumerable = MyThing.DequeueAsEnum」,然後在兩個不相交的For-Each循環中使用MyIEnumerable。如果DequeueAsEnum返回一個EnumerableOnlyOnce類型,那麼它會更清楚一點,它的返回值只應枚舉一次。可以肯定的是,在新的C#和VB.Net方言中隱式輸入的存在使得更有可能有人可能會在不應該的時候將函數返回賦值給變量,但我不知道如何防止這種情況。

順便說一句,有很多情況下,防止將類引用存儲到變量中會有所幫助;有沒有辦法以外部代碼可以使用該類的表達式的方式聲明一個類,但不能聲明它的變量?

相關問題