.net 为什么队列(T)和堆栈(T)不实现ICollection(T)?
在我提问之前,让我先给出一个明显的答案:.net 为什么队列(T)和堆栈(T)不实现ICollection(T)?,.net,interface,stack,queue,icollection,.net,Interface,Stack,Queue,Icollection,在我提问之前,让我先给出一个明显的答案:ICollection接口包括一个Remove方法来删除任意元素,而Queue和Stack实际上不支持该方法(因为它们只能删除“end”元素) 好吧,我意识到了。实际上,我的问题不是关于队列或堆栈集合类型的;相反,它是关于不为本质上是T值集合的任何泛型类型实现ICollection的设计决策 我觉得奇怪的是。假设我有一个接受任意T集合的方法,为了编写代码,了解集合的大小会很有用。例如(下面的代码很简单,仅用于说明!): 完全放弃ICollection接口不
ICollection
接口包括一个Remove
方法来删除任意元素,而Queue
和Stack
实际上不支持该方法(因为它们只能删除“end”元素)
好吧,我意识到了。实际上,我的问题不是关于队列
或堆栈
集合类型的;相反,它是关于不为本质上是T
值集合的任何泛型类型实现ICollection
的设计决策
我觉得奇怪的是。假设我有一个接受任意T
集合的方法,为了编写代码,了解集合的大小会很有用。例如(下面的代码很简单,仅用于说明!):
完全放弃ICollection
接口不是更有意义吗(当然,我不是说放弃实现,因为这将是一个突破性的改变;我只是说,停止使用它),只需为没有完美匹配的成员显式实现ICollection
我的意思是,看看ICollection
提供了什么:
--计数
和队列
都有此功能堆栈
--IsReadOnly
和队列
很容易就能做到这一点堆栈
-Add
可以明确地实现这一点(使用Queue
),也可以使用Enqueue
(使用Stack
)Push
——检查清除
——检查包含
——检查CopyTo
——检查(duh)GetEnumerator
——这是唯一一个Remove
和Queue
没有完美匹配的Stack
ICollection.Remove
返回一个bool
;因此,Queue
的显式实现可以完全(例如)检查要删除的项目是否实际上是head元素(使用Peek
),如果是,则调用Dequeue
并返回true
,否则返回false
<代码>堆栈可以很容易地通过Peek
和Pop
得到类似的实现
好了,既然我已经写了大约一千字来解释为什么我认为这是可能的,我提出了一个显而易见的问题:为什么队列和堆栈的设计者没有实现这个接口?
也就是说,设计因素是什么(我可能没有考虑到)这导致了一个决定,这将是一个错误的选择?为什么要改为实施ICollection
我想知道,在设计我自己的类型时,有什么指导原则,我应该考虑的接口实现,我可能忽略这个问题。例如,显式实现通常不完全支持的接口是否被认为是一种不好的做法(如果是这样,这似乎与实现IList有冲突,例如,List
)?队列/堆栈的概念与ICollection
的含义之间是否存在概念上的脱节
基本上,我感觉到一定有一个很好的理由Queue
(例如)不实现ICollection
,我不想盲目地设计自己的类型,以不适当的方式实现接口,而不知道自己在做什么,也不想完全思考自己在做什么
对于这个超长的问题,我深表歉意。说到底,也许它们不是理想的搭配;如果您想要列表或集合-请使用
列表
或集合
;p
ReAdd
-有一个隐含的假设,即Add
会添加到集合的末尾,这对于堆栈来说是不正确的(尽管它是针对队列的)。在某种程度上,堆栈/队列的枚举器不退出队列/pop实际上让我感到困惑,因为我基本上希望队列/堆栈中的项目每次提取一次(并且只提取一次)
也可能(再次,以我的枚举器为例)人们根本无法就在某些情况下它应该如何表现达成一致,如果没有完全一致,干脆不实施它们是更好的选择。我无法给出“实际想法是什么”答案-也许其中一位设计师会给我们真正的想法,我可以删除这个
然而,把自己放在“如果有人来找我做这个决定怎么办”的心态中,我可以想出一个答案。。让我用这段代码来说明:
public void SomeMethod<T>( ICollection<T> collection, T valueA, T valueB)
{
collection.Add( valueA);
collection.Add( valueB);
if( someComplicatedCondition())
{
collection.Remove(valueA);
}
}
public void somethod(ICollection集合、T值A、T值B)
{
收款。增加(价值a);
收款。增加(增值b);
if(somecompleddcondition())
{
收集。移除(valueA);
}
}
(当然,任何人都可以创建一个糟糕的ICollection
实现,但我们希望框架能够树立榜样)。让我们假设堆栈/队列实现如您在问题中所述。那么,是右上方的代码,还是因为应该选中ICollection.Remove()
,所以它有边缘大小写错误?如果必须删除valueA
,如何解决此问题以同时使用堆栈和队列?答案是有的,但显然,在这种情况下,上面的代码是错误的——尽管它闻起来很合理
因此,这两种解释都是有效的,但我对这里所做的决定很满意——如果我有上面的代码,并且知道我可以被传递一个队列或堆栈,我可以围绕它进行设计,但这肯定是一个很容易陷入的bug坑(无论你在哪里看到ICollection
,记住边缘cas)
// 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!");
}
public void SomeMethod<T>( ICollection<T> collection, T valueA, T valueB)
{
collection.Add( valueA);
collection.Add( valueB);
if( someComplicatedCondition())
{
collection.Remove(valueA);
}
}
public interface IPeekable<T> : IEnumerable<T> // or IInOut<T> or similar
{
int Count { get; }
bool Contains(T item);
T Peek();
void Add(T item);
bool Remove();
void Clear();
}