在我问之前,让我得到一个明显的答案:接口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>
容易拥有这个。Add
--Queue<T>
可以明确地实现这一点(withEnqueue
),也可以Stack<T>
(withPush
)。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>
,并且我不想只是盲目地设计自己的类型并以不适当的方式实现接口而没有被告知和充分考虑我在做什么。
我为这个超长的问题道歉。