代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| namespace Test
{
public interface IMyClass
{
List <IMyClass > GetList ();
}
public class MyClass : IMyClass
{
public List <IMyClass > GetList ()
{
return new List <IMyClass >();
}
}
} |
当我运行代码分析时,我得到了以下建议。
Warning 3 CA1002 : Microsoft.Design : Change 'List' in 'IMyClass.GetList()' to use Collection, ReadOnlyCollection or KeyedCollection
我应该如何解决这个问题?这里的好做法是什么?
为了回答问题的"为什么"部分,即为什么不使用List,原因是未来验证和API的简单性。
未来打样
List的设计不是为了通过对其子类化而易于扩展;它的设计是为了快速地进行内部实现。您会注意到它上的方法不是虚拟的,因此不能被重写,并且它的Add/Insert/Remove操作没有钩子。
这意味着,如果将来需要更改集合的行为(例如,拒绝人们试图添加的空对象,或者在发生这种情况时执行其他工作,例如更新类状态),则需要更改返回到可以子类的集合的类型,这将是一个中断的接口更改(C我们更改诸如不允许空值之类的东西的语义也可能是一个接口更改,但是像更新内部类状态这样的事情是不允许的)。
因此,通过返回一个很容易被子类化的类(如Collection)或一个接口(如IList、ICollection或IEnumerable,您可以将内部实现更改为不同的集合类型以满足您的需要,而不必破坏消费者的代码,因为它仍然可以作为他们所期望的类型返回。叮叮声。
简单API
List包含了很多有用的操作,如BinarySearch和Sort等。但是,如果这是一个您要公开的集合,那么很可能您控制了列表的语义,而不是使用者。因此,虽然您的类在内部可能需要这些操作,但类的消费者不太可能(甚至应该)调用它们。
因此,通过提供一个更简单的集合类或接口,可以减少API用户看到的成员数量,并使其更易于使用。
- 这是一个死气沉沉的回应。另一篇关于这个主题的好文章可以在这里找到:blogs.msdn.com/fxcop/archive/2006/04/27/&hellip;
- 我明白你的第一点,但我不知道我是否同意你的API简单性部分。
- stackoverflow.com/a/398988/2632991这也是一篇很好的文章,关于集合和列表之间的区别。
- blogs.msdn.com/fxcop/archive/2006/04/27/&hellip;->已经死了。我们有新的信息吗?
- 工作链接:blogs.msdn.microsoft.com/kcwalina/2005/09/26/&hellip;
我将亲自声明它以返回接口而不是具体的集合。如果您真的想要访问列表,请使用IList。否则,考虑ICollection和IEnumerable。
- 然后IList会扩展ICollection接口吗?
- 是的,IList扩展了ICollection。我将添加文档链接。
- @乔恩:我知道这很古老,但是你能在blogs.msdn.com/b/kcwalina/archive/2005/09/26/474010.aspx上评论一下Krzysztof的话吗?具体来说,他的评论,江户十一〔0〕CA1002似乎与Krzysztof的评论一致。我无法想象为什么要推荐一个具体的集合而不是一个接口,以及为什么要区分输入/输出。
- @纳尔逊:很少有人要求呼叫者传递不可变的列表,但是返回一个列表是合理的,这样他们就知道它是绝对不可变的。但不确定其他收藏。如果有更多的细节就好了。
- 这不是针对具体案件的。显然,一般情况下,ReadOnlyCollection对于输入没有意义。同样地,作为输入,IList表示"我需要sort()或IList拥有的其他成员",这对于输出来说是没有意义的。但我的意思是,为什么推荐ICollection作为输入,Collection作为输出。为什么不按照您的建议使用ICollection作为输出?
- @尼尔森:老实说,我不知道。最好能从Krzysztof那里得到更多的信息。
- 我认为这与不含糊有关。Collection和ReadOnlyCollection都来源于ICollection(即没有IReadOnlyCollection)。如果您返回接口,那么它是哪一个以及是否可以修改并不明显。不管怎样,谢谢你的意见。这对我来说是一张很好的健康支票。
还有一些需要补充的东西,尽管这已经很久没有被问到了。
当您的列表类型从List而不是Collection派生时,您不能实现Collection实现的受保护的虚拟方法。这意味着,如果对列表进行了任何修改,则派生类型将无法响应。这是因为List假设您在添加或删除项时知道。能够响应通知是一种开销,因此List不提供它。
在外部代码可以访问集合的情况下,您可能无法控制添加或删除项的时间。因此,Collection提供了一种方法来了解您的列表何时被修改。
在这种情况下,我通常尝试公开所需的最少数量的实现。如果消费者不需要知道您实际上正在使用列表,那么您就不需要返回列表。通过返回Microsoft建议的集合,您可以隐藏这样一个事实:您使用的是一个来自类消费者的列表,并将其与内部更改隔离开来。
它主要是抽象出您自己的实现,而不是将列表对象直接暴露出来进行操作。
让其他对象(或人)直接修改对象的状态是不好的做法。想想财产获得者/设定者。
采集->正常采集readOnlyCollection->用于不应修改的集合keyedcollection->当您需要字典时。
如何修复它取决于您希望类做什么以及getList()方法的用途。你能详细解释一下吗?
- 但是collection和keyedcollection也不是只读的。
- @纳法尔,我怎么说的?
- 您声明不允许其他对象(或人)直接修改对象的状态并列出3种集合类型。除非以东十一〔十二〕二人不服从。
- 这是指"良好实践"。请提供适当的上下文。下面的列表简单地说明了这类类型的基本要求,因为OP想要了解警告。然后我继续问他关于以东的目的(13),以便能够更正确地帮助他。
- 我没有说过,使用这三种类型会立即让你进入"良好实践"的位置。
- 好吧,我明白这一点。但我认为推理部分仍然不健全。你说Collection有助于抽象内部实现,并防止直接操纵内部列表。怎样?Collection只是一个包装器,在传递的同一个实例上操作。它是一个动态集合,用于继承,而不是其他任何东西(Greg的答案在这里更相关)。
- 让我们在聊天中继续讨论。
我认为还没有人回答"为什么"这一部分…所以这里是。为什么"你"应该使用Collection而不是List,是因为如果你公开List,那么任何能够访问你的对象的人都可以修改列表中的项目。而Collection应该表示您正在创建自己的"添加"、"删除"等方法。
你可能不需要担心,因为你可能只是为自己(或者一些同事)编写接口。下面是另一个可能有意义的例子。
如果有公共数组,例如:
1
| public int[] MyIntegers { get; } |
您可能会认为,因为只有一个"get"访问器,没有人可以处理这些值,但这不是真的。任何人都可以这样改变里面的值:
1
| someObject.MyIngegers[3] = 12345; |
就我个人而言,大多数情况下我只使用List。但是如果你正在设计一个类库,你要把它交给随机的开发人员,你需要依赖于对象的状态…那么你就要自己制作收藏,并从那里锁定它:)
- "如果将list返回到客户端代码,则当客户端代码修改集合时,将无法接收通知。"-fx cop…另请参阅"msdn.microsoft.com/en-us/library/0fss9skc.aspx"…哇,看来我还没下马呢:)
- 哇-几年后,我看到我贴在上面评论的链接末尾有一个引用,所以它不起作用…msdn.microsoft.com/en-us/library/0fss9skc.aspx
我看不出退货有什么问题
1
| this.InternalData.Filter(crteria).ToList(); |
如果我返回一个断开连接的内部数据副本,或者一个数据查询的分离结果——我可以安全地返回List,而不暴露任何实现细节,并允许以方便的方式使用返回的数据。
但这取决于我所期望的消费者类型——如果这是类似于数据网格的东西,我宁愿返回IEnumerable,在大多数情况下,这将是项目的复制列表:)
集合类实际上只是围绕其他集合的包装类,用来隐藏它们的实现细节和其他特性。我认为这与面向对象语言中隐藏编码模式的属性有关。
我认为您不应该担心它,但是如果您真的想取悦代码分析工具,只需执行以下操作:
1 2 3
| //using System.Collections.ObjectModel;
Collection <MyClass > myCollection = new Collection <MyClass >(myList ); |
- 由于一般不变性,这不起作用:(
- 对不起,那是个打字错误。i menat collection。我真的很期待能接触到C 4和一般协方差btw!
- 据我所知,用EDOCX1[4]包装既不保护自身也不保护底层集合。
- 这段代码是为了"取悦代码分析工具"。我不认为@tamasczinege在任何地方说使用Collection会立即保护您的基础收藏。