【发布时间】:2011-06-14 03:45:20
【问题描述】:
在我问之前,让我得到一个明显的答案:ICollection<T> 接口包含一个 Remove 删除任意元素的方法,Queue<T> 和 Stack<T> 不能真的支持(因为他们只能删除“结束”元素)。
好的,我意识到这一点。实际上,我的问题并不是专门针对 Queue<T> 或 Stack<T> 集合类型;相反,它是关于不为 any 本质上是 T 值集合的泛型类型实现 ICollection<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;
}
}
}
现在,除了这些类型没有实现 ICollection<T> 之外,没有理由不能在 Queue<T> 或 Stack<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-- 检查 (duh)。 -
Remove-- 这是唯一一个Queue<T>和Stack<T>没有完美匹配的。
这是真正的关键:ICollection<T>.Remove 返回一个 bool;因此Queue<T> 的显式实现可以完全(例如)检查要删除的项目是否实际上是头元素(使用Peek),如果是,则调用Dequeue并返回true,否则返回false。使用Peek 和Pop 可以轻松地为Stack<T> 提供类似的实现。
好吧,既然我已经写了大约一千字来说明为什么我认为这是可能的,我提出一个显而易见的问题:为什么没有 em> Queue<T> 和 Stack<T> 的设计者实现了这个接口? 也就是说,是什么设计因素(我可能没有考虑)导致决定这是错误的选择?为什么改为实现ICollection?
我想知道,在设计我自己的 类型时,我是否应该考虑关于接口实现的任何指导原则,而我在提出这个问题时可能会忽略这些指导原则。例如,显式实现通常不完全支持的接口是否被认为是不好的做法(如果是这样,这似乎与List<T>实现IList相冲突)?队列/堆栈的概念与ICollection<T> 所代表的含义之间是否存在概念脱节?
基本上,我觉得Queue<T>(例如)没有实现ICollection<T>,我不想只是盲目地向前设计我的自己的类型和以不适当的方式实现接口,而没有被告知并充分考虑我在做什么。
对于这个超长的问题,我深表歉意。
【问题讨论】:
-
这只是 MS 糟糕的收藏库的另一个例子。这个问题可以通过至少将接口分为只读和读写版本(读写版本从只读版本继承)以及更细粒度的接口来解决,只关注一个集合的特定方面,而不是包含不相关的属性和方法。所以 ICollection
不应该包含 Add、Clear 或 Remove。这应该留给例如 IMutableCollection 。 ICollection 可以由更多类实现。 -
@siride:您对 MS 馆藏库的看法与我的相同。令人讨厌的是,索引属性之类的东西必须为只读和读写版本定义两次,但这就是生活。
-
+1 用于实现
Remove的创新,这很酷 :) 不,这个超长的 q 写得很好。
标签: .net interface stack queue icollection