为什么Queue(T)和Stack(T)没有实现ICollection(T)?

Dan*_*Tao 15 .net queue stack interface icollection

之前,我甚至问,让我得到了明显的答案的方式进行: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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,除了那些类型没有实现之外,没有理由为什么这个代码不能在a Queue<T>或a 上运行.当然,他们确实实现了 - 我主要是为了单独测试属性 - 但是这会导致像这样的奇怪的优化代码:Stack<T>ICollection<T>ICollectionCount

// 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!");
}
Run Code Online (Sandbox Code Playgroud)

完全放弃ICollection界面是不是更有意义(当然,我并不是说放弃实现,因为这将是一个突破性的变化;我只是意味着,停止使用它),并且只需实现ICollection<T>显式实现没有完美匹配的会员?

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

  • Count- Queue<T>并且Stack<T>都有这个.
  • IsReadOnly- Queue<T>Stack<T>轻松地可能都没有了.
  • Add- Queue<T>可以(with Enqueue)显式实现Stack<T>(with Push).
  • Clear - 检查.
  • Contains - 检查.
  • CopyTo - 检查.
  • GetEnumerator - 检查(duh).
  • Remove- 这是唯一的一个Queue<T>,并Stack<T>没有一个完美的匹配.

这是真正的踢球者:ICollection<T>.Remove 返回一个bool ; 所以一个显式的实现Queue<T>可以完全(例如)检查要删除的项是否实际上是head元素(使用Peek),如果是,则调用Dequeue并返回true,否则返回false.Stack<T>可以很容易地给出类似的实现PeekPop.

好了,现在,我已经写了一万字,为什么觉得这是有可能,我提出一个明显的问题:为什么没有的设计者Queue<T>Stack<T>实现这个接口?也就是说,设计因素(我可能没有考虑过)导致决定这是错误的选择是什么?为什么要ICollection实施呢?

我想知道,在设计我自己的类型时,是否有任何指导原则我应该考虑接口实现,我可能会在提出这个问题时忽略.例如,仅仅考虑不好的做法是明确地实现一般不完全支持的接口(如果是这样,这似乎与例如List<T>实现相冲突IList)?队列/堆栈的概念与代表什么之间存在概念上的脱节ICollection<T>

基本上,我觉得必须有一个很好的理由Queue<T>(例如)没有实现ICollection<T>,我不想盲目地设计我自己的类型并以不恰当的方式实现接口而不需要通知和充分思考我在做什么

我为超长问题道歉.

Phi*_*eck 6

我不能给出"实际思考是什么"的答案 - 也许其中一位设计师会给我们真正的思考,我可以删除它.

然而,让自己置身于"如果有人来找我做出这个决定"的心态,我可以想到一个答案..让我用这段代码来说明:

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

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

  if( someComplicatedCondition())
  {
    collection.Remove(valueA);
  }
}
Run Code Online (Sandbox Code Playgroud)

(当然,任何人都可以创建一个糟糕的实现ICollection<T>,但我们期望框架设置示例).让我们假设Stack/Queue在您在问题中陈述时实现.那么上面的代码是正确的,还是有边缘案例错误,因为ICollection<T>.Remove()应该检查?如果valueA必须删除,我该如何解决这个问题,以便与Stack Queue一起使用?有答案,但显然上面的代码在这种情况下是错误的 - 即使它闻起来很合理.

所以这两种解释都是有效的,但是我很擅长这里做出的决定 - 如果我有上面的代码,并且知道我可以传递一个我可以围绕它设计的队列或堆栈,但它肯定会是一个容易的错误落入(你看到的任何地方ICollection<T>,记住要删除的边缘情况!)