为什么.NET Framework中没有IQueue<T>或IStack<T>接口?

40

我想写一个基于队列接口的对象。我搜索了一下 IQueue<T>,但出乎意料地毫无结果。

以下是与集合相关的接口:

  • ICollection<T>
  • IList<T>
  • IDictionary<TKey,TValue>
  • ISet<T>

为什么这些类型的集合有明确定义的接口?为什么栈和队列不需要接口呢?


4
栈或队列是已排序列表的一种相当简单的实现,那么为什么要构建一个像两行代码一样的东西,使它可以变成只需一行代码呢? - Russell Steen
5
具体实现细节不应成为定义良好的接口的因素。 - Paul Turner
3
优秀API设计的标志不在于你添加了什么,而在于你敢于省略了什么。 - Hans Passant
7
伟大的API设计的特点是可用性,而不仅仅是包含或排除什么! - LukeH
2
我希望有一个 IQeue<T>,这样我就可以在重构使用队列的代码时替换标准实现(例如:线程安全,预分配最大容量和无动态分配)。我相信真正的原因只是设计和实现优先级的问题。对我来说感觉像是一个疏忽,哎呀。 - yoyo
显示剩余4条评论
6个回答

14

嗯,因为那些接口与实现它们的类之间存在一对一的映射(除了最新的ConcurrentQueue)。但其他提到的接口有许多实现它们的类。

稍微扩展一下。我们无法了解BCL团队的真正动机。但我有一种感觉,当他们创建QueueStack这两个类时,这些类看起来如此平凡,以至于他们决定不将它们抽象成接口。原因很明显:那些类看起来既简单又完整,所以没有理由在BCL中创建不同的实现。然后就出现了ConcurrentQueue,前面的说法变得略微不正确。

正如所指出的,QueueConcurrentQueue并没有太多相似之处,可以使它们实现单个接口。


+1 抵消踩票。我想看到更详细的答案,但我不认为它应该被评为-1。 - Paul Sasik
1
@Paul Sasik 谢谢,我扩展了。 - Andrey
2
ConcurrentQueue<T>Queue<T> 的语义(大多数情况下)也有所不同;它们唯一真正共享的抽象是 Enqueue - Jeff Sternal
我希望有一个接口,可以同时实现栈和队列,以便轻松创建LIFO FIFO行为。void Add(T), T Take(), int Count { get(); }。然后我可以在最小最大堆中实现它,以获得更多的灵活性。 - Bas Smit
@BasSmit 没有任何阻止你这样做的事情。通用接口唯一给你的是可以传递不同的实现。 - Andrey
显示剩余3条评论

5
我认为.NET 4中新增的ConcurrentQueue<T>ConcurrentStack<T>类的存在证明了您的观点,即应该有IQueue<T>IStack<T>接口。 即使它们最初不存在,也应在.NET 4中添加它们。
我过去在.NET中创建自己的自定义堆栈实现,并认同拥有一个标准接口将会非常有益。

4
我猜测你没有看到栈或队列接口是因为它们的集合性质。你列出的集合接口是随机访问的,而栈和队列不是。它们必须以非常特定的方式访问(FIFO或LIFO),因此不能通常使用。因此,只使用这些集合作为通用实现,而不通过接口。换句话说,由于LIFO/FIFO对集合施加的限制,实现理论上的IStack或IQueue的类中几乎没有任何可以做出不同的功能(或没有任何功能)。
为了验证这个想法,你可以创建自己的IStack和/或IQueue,然后在一个类中实现它们。你会发现你暴露的功能是完全的传递。例如,你最终将简单地转发或返回请求和结果到一个内部的栈或队列集合中。并且那种传递功能很少或没有任何潜在的增值功能。

3
ConcurrentQueue<T>是否会让Queue<T>和它实现一个共同的接口更有意义?请注意,ConcurrentQueue<T>目前在BCL中已经存在。 - LukeH
@LukeH: 这是一个更加专门的集合,尽管它仍然遵循FIFO语义,所以我认为iConcurrentQueue接口并不真正有用。 - Paul Sasik
4
我将会创建一个IQueue<T>接口!我的实现将使用同步存储并持久化到磁盘。如果能够重用已有的接口而不是定义自己的接口,那就更好了! - Paul Turner
1
也许不是,但是在Queue<T>ConcurrentQueue<T>之间的常见抽象仍然很有用,即使它们只是将CQ<T>作为Q<T>的真正特化。 - LukeH
都是好观点。我只是在阐述微软可能没有费心编写IQueue/IStack的原因。 - Paul Sasik

4
为什么.NET框架要有>和>接口?它们有什么用处?
这些类型的集合之所以具有明确定义的接口,是因为基类库实际上使用了所有这些接口(如IList等)。在每种情况下,还有一堆其他类需要与接口抽象而不是具体类型进行交互。
为什么堆栈和队列不需要接口?
可能是因为没有任何需要IStack和IQueue接口的BCL类,并且这些抽象的显然或可能的用例不存在。
我意识到这并不是一个令人满意的哲学答案,但从框架设计师的角度来看,他们非常务实地制定接口:只有在需要时才创建接口。

务实的方面是值得考虑的。我肯定不会为一个我没有立即使用的接口而创建它。 - Paul Turner
在我看来,这可能是“真正”的答案。我认为设计师们直到需要它们时才创建了许多包含的接口。 - KatDevsGames

2
我认为这是因为Stack和Queue是非常特殊的概念,据我所知没有其他类实现了栈或队列的特殊形式。
编辑:现在有一个ConcurrentQueue,似乎否定了我的结论。也许,在C# 1.0仍处于beta版时,语言设计者认为永远不会有队列/栈的特殊形式,现在引入接口已经太晚了。
这是很多猜测。我希望Eric Lippert能看到这个问题并发表一个“官方”声明。 :-)

@VVSпјҡе…ідәҺжӮЁзҡ„зј–иҫ‘вҖҰеңЁ.NET4дёӯеј•е…ҘSortedSet<T>ж—¶пјҢиҝҷ并没жңүйҳ»жӯўд»–们引е…ҘISet<T>жҺҘеҸЈе№¶е°Ҷе…¶йҖӮй…ҚеҲ°HashSet<T>гҖӮ - LukeH
2
原则上,DEQ应该派生自IStack和IQueue。 - annakata
@annakta:我在我的代码中从未使用过deque,所以我对它没有经验。如果将其实现为队列或堆栈,使用deque是否有意义?因为这就是您实现接口时所说的内容。 - VVS
@LukeH:同意。那个假设显然是错误的。我在这里所做的只是有根据的猜测。 - VVS
5
我对BCL的任何设计方面都没有“官方”立场。如果您想直接从团队那里得到答案,可以考虑在BCLTeam博客上提问。 - Eric Lippert
显示剩余5条评论

0

我认为没有什么特别的原因。如果我能自己选择,我会更倾向于有更多离散定义的接口,即使没有只实现它们的类。例如,我可能会实现一个QueueStack(Of T),它将实现IGetNext(Of T)、IGetNextWait(Of T)和IClear(Of T),以及IQueue(Of T)、IWaitQueue(Of T)、IStack(Of T)和IWaitStack(Of T)。使用对象的人可以明确指定他们实际需要的内容,而实现者只需实现他们声称支持的内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接