質問する前に、明白な答えを教えてください。ICollection<T>
インターフェイスにはRemove
、任意の要素を削除するメソッドが含まれていますが、これは実際Queue<T>
にStack<T>
はサポートできません (「終了」要素しか削除できないため)。
わかりました。実際、私の質問はコレクション型Queue<T>
またはStack<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;
}
}
}
さて、これらの型が を実装していないことを除いて、このコードがQueue<T>
や で動作できない理由は本当にありません。もちろん、彼らは を実装しています — 私は主にプロパティだけを推測しています — しかし、それは次のような奇妙な最適化コードにつながります: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>
簡単に手に入れることができます。Add
--これを ( を使用して)Queue<T>
明示的に実装できます( を使用)。Enqueue
Stack<T>
Push
Clear
- 小切手。Contains
- 小切手。CopyTo
- 小切手。GetEnumerator
-- 確認してください (当たり前)。Remove
--完全に一致しないのはこれだけです。Queue<T>
Stack<T>
そして、これが本当のキッカーです: ;ICollection<T>.Remove
を返します。bool
したがって、 の明示的な実装は、削除する項目が実際に head 要素Queue<T>
であるかどうかを (たとえば) 完全にチェックし( を使用)、そうであれば を呼び出して returnを、そうでなければ returnを返すことができます。と を使用して同様の実装を簡単に行うことができます。Peek
Dequeue
true
false
Stack<T>
Peek
Pop
さて、これが可能であると考える理由について約 1000 語を書いたので、明らかな疑問を提起します。なぜ、このインターフェースの設計者と実装者はこのインターフェースを実装しなかったのでしょうか? Queue<T>
Stack<T>
つまり、これが間違った選択であるという決定につながった設計要因 (おそらく考慮していない) は何でしたか? なぜICollection
代わりに実装されたのですか?
私自身の型を設計する際に、この質問をする際に見落としている可能性のあるインターフェイスの実装に関して考慮すべき指針があるかどうか疑問に思っています。たとえば、一般的に完全にサポートされていないインターフェイスを明示的に実装することは、単に悪い習慣と見なされますか (そうであれば、List<T>
実装などと競合するように見えますIList
)? キュー/スタックの概念と何を表現するかの間に概念上の断絶はありますか?ICollection<T>
Queue<T>
基本的に、かなり正当な理由(たとえば)が実装されていないに違いないと感じており、ICollection<T>
情報を得て十分に検討することなく、自分の型を設計したり、不適切な方法でインターフェイスを実装したりすることをやみくもに進めたくありません。私がしていること。
超長い質問で申し訳ありません。