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

标签 .net interface stack queue icollection

在我问之前,让我得到一个明显的答案:ICollection<T>接口(interface)包括一个Remove删除任意元素的方法,其中 Queue<T>Stack<T>不能真正支持(因为他们只能删除“结束”元素)。

好的,我意识到这一点。实际上,我的问题并不是专门针对 Queue<T>Stack<T>集合类型;而是关于不实现 ICollection<T> 的设计决策对于本质上是 T 集合的任何泛型类型值(value)观。

这就是我觉得奇怪的地方。假设我有一个方法可以接受 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接口(interface)完整 (当然,我的意思不是放弃实现,因为那将是一个重大变化;我的意思是,停止使用它),然后简单地实现 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 ;所以 Queue<T> 的显式实现可以完全(例如)检查要删除的项目是否实际上是 head 元素(使用 Peek ),如果是,请调用 Dequeue并返回 true , 否则返回 false . Stack<T>可以很容易地用 Peek 给出类似的实现和 Pop .

    好吧,既然我已经写了大约一千字来解释为什么我认为这是可能的,我提出一个显而易见的问题: Queue<T> 的设计者为什么没有和 Stack<T>实现这个接口(interface)? 也就是说,是什么设计因素(我可能没有考虑)导致决定这将是错误的选择?为什么是 ICollection改为实现?

    我想知道,在设计我自己的类型时,我是否应该考虑关于接口(interface)实现的任何指导原则,而我在提出这个问题时可能会忽略这些指导原则。例如,显式实现通常不完全支持的接口(interface)是否被认为是不好的做法(如果是这样,这似乎与,例如 List<T> 实现 IList 冲突)?队列/堆栈的概念与ICollection<T> 的概念之间是否存在概念上的脱节?是为了代表?

    基本上,我觉得肯定有一个很好的理由Queue<T> (例如)没有实现 ICollection<T> ,并且我不想只是盲目地设计自己的类型并以不适当的方式实现接口(interface),而没有了解并充分考虑我在做什么。

    我为这个超长的问题道歉。

    最佳答案

    我不能给出“实际想法是什么”的答案——也许其中一位设计师会给我们真正的想法,我可以删除它。

    但是,将自己置于“如果有人来找我做出这个决定怎么办”的心态,我可以想到一个答案..让我用这段代码来说明:

    public void SomeMethod<T>( ICollection<T> collection, T valueA, T valueB)
    {
    
      collection.Add( valueA);
      collection.Add( valueB);
    
      if( someComplicatedCondition())
      {
        collection.Remove(valueA);
      }
    }
    

    (当然,任何人都可以创建 ICollection<T> 的错误实现,但我们希望框架能够树立榜样)。让我们假设您在问题中陈述的 Stack/Queue 实现。上面的代码也是如此,还是因为 ICollection<T>.Remove() 存在边缘情况错误?应该检查吗?如果 valueA必须删除,如何解决此问题以同时使用堆栈和队列?有答案,但显然上面的代码在这种情况下是错误的——即使它闻起来很合理。

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

    关于.net - 为什么 Queue(T) 和 Stack(T) 没有实现 ICollection(T)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4776218/

    相关文章:

    c++ - 在 C++ 中将值分配给另一个堆栈

    .net - 枚举错误提供程序中的错误

    c# - 如何在只有 StartCopyFromBlob() 的 Azure SDK 中实现单个 blob 的同步复制?

    java - 从我的界面找不到方法

    java - 如何返回类型接口(interface)

    c# - 使 C# 方法 "implement"成为委托(delegate)

    c - 解析堆栈和寄存器(EBP、EIP、ESP)并找出与每个帧相关的函数

    java - 使用堆栈错误进行后缀评估(java)

    c# - 根据登录条件停止?

    .net - nuget 是否有一种解决方案来包含依赖于一个包的不同版本的包?