29

遵循公共 API 永远不应该返回列表的规则,我正在盲目转换所有返回列表的代码,以返回ICollection<T>

public IList<T> CommaSeparate(String value) {...}

变成

public ICollection<T> CommaSeparate(String value) {...}

尽管 anICollection具有Count,但无法通过该索引获取项目。

尽管 anICollection公开了一个枚举器(允许foreach),但我认为不能保证枚举的顺序从列表的“顶部”开始,而不是“底部”。

我可以通过避免使用来缓解这种情况ICollection,而是使用Collection

public Collection<T> Commaseparate(String value) {...}

这允许使用Items[index]语法。

不幸的是,我的内部实现构造了一个数组;可以将其转换为 return IListor ICollection,但不能转换为Collection.

有没有办法按顺序访问集合中的项目?

这就引出了一个更广泛的问题:ICollection甚至订单吗?


从概念上讲,假设我想解析一个命令行字符串。保持项目的顺序至关重要。

从概念上讲,我需要一个指示“有序”字符串元组集的合同。在 API 合约的情况下,为了指示顺序,以下哪项是正确的:

IEnumerable<String> Grob(string s)

ICollection<String> Grob(string s)

IList<String> Grob(string s)

Collection<String> Grob(string s)

List<String> Grob(string s)
4

9 回答 9

19

ICollection<T>界面没有指定任何关于订单的内容。对象将按照返回对象指定的顺序排列。例如,如果您返回Valuesa 的集合SortedDictionary,则对象将按照字典比较器定义的顺序排列。

如果您需要该方法通过协定返回其协定需要特定顺序的类型,那么您应该通过返回更具体的类型在方法的签名中表达这一点。

无论返回对象的运行时类型如何,请考虑静态引用为IList<T>or时的行为ICollection<T>:当您调用时GetEnumerator()(可能在 foreach 循环中隐式调用),您将调用相同的方法并获取相同的对象,而不管静态参考的类型。因此,无论CommaSeparate()方法的返回类型如何,它的行为方式都相同。

补充思想:

正如其他人指出的那样,FXCop 规则警告不要使用List<T>, not IList<T>; 您链接到的问题是询问为什么 FXCop 不建议使用IList<T>代替List<T>,这是另一回事。如果我想象你正在解析一个顺序很重要的命令行字符串,IList<T>如果我是你,我会坚持下去。

于 2012-01-05T21:44:47.507 回答
10

ICollection 没有保证的顺序,但实际实现它的类可能(也可能没有)。

如果你想返回一个有序的集合,那么返回一个IList<T>并且不要太沉迷于 FxCop 的一般声音,但非常通用的建议。

于 2012-01-05T21:45:09.527 回答
4

ICollection实例具有实现它的任何类的“顺序”。也就是说,将 a 引用List<T>为 anICollection根本不会改变它的顺序。

同样,如果您将无序集合作为 访问ICollection,它也不会对无序集合施加顺序。

所以,对于你的问题:

ICollection 甚至有订单吗?

答案是:它完全取决于实现它的类。

于 2012-01-05T21:45:06.717 回答
4

不,ICollection 并不意味着订单。

于 2012-01-05T21:46:04.417 回答
3

ICollection只是一个接口——没有关于排序的实现或显式规范。这意味着,如果您返回以有序方式枚举的东西,那么无论消耗什么,您ICollection都将以有序的方式这样做。

顺序仅由底层的实现对象隐含。没有说明ICollection是否应该订购它。多次枚举结果将调用底层对象的枚举器,这是设置这些规则的唯一位置。一个对象不会因为继承了这个接口而改变它的枚举方式。如果接口指定它是有序结果,那么您可以安全地依赖实现对象的有序结果。

于 2012-01-05T21:46:16.007 回答
3

AnICollection<T>只是一个接口;它是否被订购完全取决于它的底层实现(应该是不透明的)。

如果您希望能够通过索引访问它,您希望将事物返回为 an IList<T>,这两者都是IEnumerable<T>and ICollection<T>。但是,应该记住,根据底层实现,获取集合中的任意项目可能平均需要 O(N/2) 时间。

我的倾向是完全避免使用“集合”接口,而是使用自定义类型来表示问题域方面的集合,并公开适合该类型的适当逻辑操作。

于 2012-01-05T21:46:32.097 回答
3

ICollection<T>可能有顺序,但实际顺序取决于实现它的类。它没有给定索引处的项目的访问器。IList<T>专门此接口以提供按索引访问。

于 2012-01-05T21:46:41.780 回答
1

这取决于实例的实现。碰巧是 List 的 ICollection 有顺序,碰巧是 Collection 的 ICollection 没有。

所有 ICollections 都实现了 IEnumerable,它一次返回一个项目,无论是有序的还是其他的。

编辑:在回复您在问题中有关命令行解析的附加示例时,我认为适当的返回类型取决于您之后对这些参数所做的事情,但 IEnumerable 可能是正确的。

我的理由是 IList、ICollection 和它们的具体实现允许修改从 Grob 返回的列表,您可能不希望这样做。由于 .NET 没有索引序列接口,IEnumerable 是防止调用者做一些奇怪的事情的最佳选择,比如尝试修改他们返回的参数列表。

于 2012-01-05T21:45:30.593 回答
-1

如果您希望您的方法的所有当前版本和未来版本都可以轻松返回能够快速轻松地检索第 N 个项目的对象,请使用 typeIList<T>返回对实现IList<T>和 non-generic的引用ICollection。如果您希望某些当前或将来的版本可能无法快速轻松地返回第 N 个项目,但能够立即报告项目的数量,请使用 typeICollection<T>返回引用的东西,实现ICollection<T>和非泛型ICollection。如果您认为现在或将来的版本即使知道有多少项目也可能会遇到问题,请返回IEnumerable<T>。排序问题无关紧要;访问第 N 个事物的能力意味着存在定义的序列,但是ICollection<T>关于排序的说法不多也不少IEnumerable<T>

于 2013-12-18T18:12:12.313 回答