Dot-Net

為什麼 Queue(T) 和 Stack(T) 沒有實現 ICollection(T)?

  • June 8, 2014

在我問之前,讓我得到一個明顯的答案:介面ICollection<T>包含一個Remove刪除任意元素的方法,它Queue<T>不能Stack<T>真正支持(因為它們只能刪除“結束”元素)。

好的,我意識到這一點。實際上,我的問題並不是專門針對Queue<T>orStack<T>集合類型。相反,它是關於不為本質上是值集合的任何泛型類型實現ICollection<T>的設計決策T

這就是我覺得奇怪的地方。假設我有一個接受任意集合的方法T,並且出於我正在編寫的程式碼的目的,了解集合的大小會很有用。例如(以下程式碼很簡單,僅用於說明!):

// Argument validation omitted for brevity.
static IEnumerable<T> FirstHalf<T>(this ICollection<T> source)
{
   int i = 0;
   foreach (T item in source)
   {
       yield return item;
       if ((++i) >= (source.Count / 2))
       {
           break;
       }
   }
}

現在,這段程式碼真的沒有理由不能對 aQueue<T>或 a進行操作Stack<T>,只是這些類型沒有實現ICollection<T>。當然,他們確實實現ICollection了——我猜主要是為了這個Count屬性——但這會導致奇怪的優化程式碼,如下所示:

// OK, so to accommodate those bastard Queue<T> and Stack<T> types,
// we will just accept any IEnumerable<T>...
static IEnumerable<T> FirstHalf<T>(this IEnumerable<T> source)
{
   int count = CountQuickly<T>(source);
   /* ... */
}

// Then, assuming we've got a collection type with a Count property,
// we'll use that...
static int CountQuickly<T>(IEnumerable collection)
{
   // Note: I realize this is basically what Enumerable.Count already does
   // (minus the exception); I am just including it for clarity.
   var genericColl = collection as ICollection<T>;
   if (genericColl != null)
   {
       return genericColl.Count;
   }

   var nonGenericColl = collection as ICollection;
   if (nonGenericColl != null)
   {
       return nonGenericColl.Count;
   }

   // ...or else we'll just throw an exception, since this collection
   // can't be counted quickly.
   throw new ArgumentException("Cannot count this collection quickly!");
}

完全放棄ICollection介面不是更有意義嗎(當然,我並不是說放棄實現,因為這將是一個重大變化;我只是說,停止使用它),並簡單地ICollection<T>用顯式實現來實現沒有完美匹配的成員?

我的意思是,看看ICollection<T>提供了什麼:

  • Count-Queue<T>兩者Stack<T>都有這個。
  • IsReadOnly-Queue<T>並且Stack<T>容易擁有這個。
  • AddQueue<T>可以明確地實現這一點(with Enqueue),也可以Stack<T>(with Push)。
  • Clear- 查看。
  • Contains- 查看。
  • CopyTo- 查看。
  • GetEnumerator——檢查(呃)。
  • Remove——這是唯一一個沒有完美匹配的人Queue<T>Stack<T>

這是真正的踢球者:ICollection<T>.Remove 返回 abool ; 所以一個顯式的實現Queue<T>可以完全(例如)檢查要刪除的項目是否實際上是頭元素(使用Peek),如果是,則呼叫Dequeue並返回true,否則返回false。可以很容易地用和Stack<T>給出類似的實現。Peek``Pop

好吧,既然我已經寫了大約一千字關於為什麼認為這是可能的,我提出一個明顯的問題:**為什麼**設計者沒有實現這個介面?Queue<T>``Stack<T>**也就是說,是什麼設計因素(我可能沒有考慮)導致決定這將是錯誤的選擇?為什麼ICollection改為實施?

我想知道,在設計我自己的類型時,我是否應該考慮關於介面實現的任何指導原則,而我在提出這個問題時可能會忽略這些指導原則。例如,顯式實現通常不完全支持的介面是否只是被認為是不好的做法(如果是這樣,這似乎與例如List<T>實現衝突IList)?隊列/堆棧的概念與所代表的內容之間是否存在概念上的脫節ICollection<T>

基本上,我覺得必須有一個很好的理由Queue<T>(例如)實現ICollection<T>,並且我不想只是盲目地設計自己的類型並以不適當的方式實現介面而沒有被告知和充分考慮我在做什麼。

我為這個超長的問題道歉。

我不能給出“實際想法是什麼”的答案——也許其中一位設計師會給我們真正的想法,我可以刪除它。

但是,將自己置於“如果有人來找我做出這個決定怎麼辦”的心態,我可以想到一個答案..讓我用這段程式碼來說明:

public void SomeMethod<T>( ICollection<T> collection, T valueA, T valueB)
{

 collection.Add( valueA);
 collection.Add( valueB);

 if( someComplicatedCondition())
 {
   collection.Remove(valueA);
 }
}

(當然,任何人都可以創建一個糟糕的實現ICollection<T>,但我們希望框架能夠樹立榜樣)。讓我們假設您在問題中陳述的 Stack/Queue 實現。那麼上面的程式碼是否正確,或者它是否有邊緣情況錯誤,因為ICollection<T>.Remove()應該檢查?如果valueA必須刪除,我該如何解決這個問題以同時使用堆棧隊列?有答案,但顯然上面的程式碼在這種情況下是錯誤的——即使它聞起來很合理。

所以這兩種解釋都是有效的,但我對這裡做出的決定很滿意——如果我有上面的程式碼並且知道我可以傳遞一個可以圍繞它設計的隊列或堆棧,但它肯定會是一個簡單的錯誤坑落入(你看到的任何地方ICollection<T>,記住刪除的邊緣情況!)

引用自:https://stackoverflow.com/questions/4776218