几乎任何集合都应该提供一种方法来允许从其他公共集合创建它,或者允许它被制成(甚至更好地被视为)其他公共集合。
数组功能强大但推理复杂,因为它们(本质上)是可变的,但允许在需要时进行一些强大的优化(例如,有效的快速排序实现确实需要能够就地交换元素)。
序列(IEnumerable<T>
对你来说)非常棒,因为它们对自身的要求很少(包括任何修改它们的关键方式),这意味着期望它们的代码往往非常灵活且广泛有用。
能够简单而轻松地从另一个集合获取特定类型的集合,您可以编写代码来处理它需要的结构,但通过简单的翻译调用轻松与许多其他类型的结构交互。如果后来证明这是一个性能问题,您可能必须避免翻译并让更多的代码库了解底层类型,但首先您可以干净而快速地编写它。
已经存在许多期望某种集合的 api,可能的原因包括:
- 写在 2.0 更改之前,这些更改使 .Net 中的集合更加丰富
- 因为他们知道他们需要改变状态,所以他们让调用者负责提供要改变的状态(比如就地排序功能)
- 他们写得不好
值得注意的是,.Net pre 2.0 中除了数组之外缺少任何类型的强类型集合,这意味着许多 BCL api 事后看来设计不佳。BCL 中的反射 api 是显着的示例,它们在应该是 IEnumerable 时返回整个地方的数组。
因此,能够轻松地从一种显式集合过渡到另一种显式集合在现实世界中非常有用。
重新阅读您的陈述(目前这不是一个真正的问题),您似乎对从一个集合转换到另一个集合时是否应该要求显式转换/转换方法感到困惑。
答案是,它取决于(并且动态确实会改变一些事情,但不会改变太多)。
简单地说,如果可以像预期类型一样透明地处理集合实例,则不需要转换。一般来说,函数应该尝试尽可能少地请求(通常为 IEnumerable),然后传入的任何实际类型都将通过多态性正常工作。在某些情况下,运行时必须做一些魔术来实现这一点(数组就是一个显着的例子)但通常这很好。
事情变得复杂的地方是功能需要具体类型的地方。同样糟糕的前期设计,例如在添加 HashSet 时未能包含 ISet 接口,这意味着如果您有一个确实需要集合的方法,您将被迫使用具体类型或滚动您自己的 ISet 和支持 Set - 不愉快。某些转换操作,例如从集合到列表,在只读上下文中是可行的(您只需确定性地枚举集合中的所有值),但在写入上下文中不可行(您可以将某些内容添加到列表两次但不能为例子)
这就是关于几乎所有不可变的通用功能约定的帮助,通过这样做,它变得更容易不关心集合的具体实现,而只考虑它的外部可见行为(集合的红黑树或一个哈希表——对消费者来说并不重要,它只是一个集合)
动态使事情变得有些复杂,因为要保持其实现的(粗略地说)“公理”,即在使用动态时,您应该得到与静态类型相同的结果,它必须处理诸如数组上的运行时魔术之类的情况。