代码如下所示:
namespace Test
{
public interface IMyClass
{
List<IMyClass> GetList();
}
public class MyClass : IMyClass
{
public List<IMyClass> GetList()
{
return new List<IMyClass>();
}
}
}
当我运行代码分析时,我得到以下建议。
警告3 CA1002:Microsoft.Design:更改'IMyClass.GetList()'中的'List'以使用Collection,ReadOnlyCollection或KeyedCollection
我该如何解决这个问题以及这里有什么好习惯?
答案 0 :(得分:212)
回答问题的“为什么”部分,为什么不是List<T>
,原因是面向未来和API简单。
<强>面向未来强>
List<T>
的目的不是通过子类化来轻松扩展;它旨在快速进行内部实施。您会注意到它上面的方法不是虚拟的,因此无法覆盖,并且Add
/ Insert
/ Remove
操作没有挂钩。
这意味着如果您将来需要更改集合的行为(例如,拒绝人们尝试添加的空对象,或者在发生这种情况时执行其他工作,例如更新类状态),那么您需要将您返回的集合类型更改为可以子类化的集合,这将是一个破坏性的接口更改(当然,更改不允许null的事物的语义也可能是接口更改,但更新内部类状态的事情不会是)。
因此,通过返回可以轻松子类化的类(如Collection<T>
)或IList<T>
,ICollection<T>
或IEnumerable<T>
等接口,您可以将内部实现更改为是一个不同的集合类型,以满足您的需求,而不会破坏消费者的代码,因为它仍然可以作为他们期望的类型返回。
API简洁性
List<T>
包含许多有用的操作,例如BinarySearch
,Sort
等。但是,如果这是您正在公开的集合,那么很可能您控制列表的语义,而不是消费者。因此,虽然您的班级内部可能需要这些操作,但您班级的消费者不太可能(甚至应该)打电话给他们。
因此,通过提供更简单的集合类或接口,您可以减少API用户看到的成员数量,并使他们更容易使用。
答案 1 :(得分:47)
我个人会声明它返回一个接口而不是一个具体的集合。如果您真的想要列表访问权限,请使用IList<T>
。否则,请考虑ICollection<T>
和IEnumerable<T>
。
答案 2 :(得分:1)
我认为没有人回答“为什么”的部分......所以这里有。 “为什么”您“应该”使用Collection<T>
而不是List<T>
的原因是因为如果您公开List<T>
,那么任何有权访问您对象的人都可以修改名单。而Collection<T>
应该表示您正在制作自己的“添加”,“删除”等方法。
您可能不需要担心它,因为您可能只为自己编写接口(或者可能是一些同事)。这是另一个可能有意义的例子。
如果你有一个公共阵列,例如:
public int[] MyIntegers { get; }
你会认为,因为只有一个“获取”访问器,没有人可以搞乱这些值,但事实并非如此。任何人都可以像这样更改其中的值:
someObject.MyIngegers[3] = 12345;
就个人而言,我会在大多数情况下使用List<T>
。但是如果你正在设计一个你要向随机开发者提供的类库,并且你需要依赖于对象的状态......那么你将需要创建自己的Collection并从那里锁定它: )
答案 3 :(得分:1)
主要是关于抽象你自己的实现,而不是直接暴露List对象。
让其他对象(或人)直接修改对象的状态并不是一种好习惯。想想物业的吸气者/制定者。
收藏 - &gt;正常收集
ReadOnlyCollection - &gt;对于不应修改的集合
KeyedCollection - &gt;当你想要字典时。
如何修复它取决于您希望您的类做什么以及GetList()方法的目的。你能详细说明吗?
答案 4 :(得分:1)
在这种情况下,我通常会尝试暴露所需的最少量的实施。如果消费者不需要知道您实际使用的是列表,则无需返回列表。通过返回,因为Microsoft建议使用Collection,您可以隐藏这样一个事实,即您正在使用类中消费者的列表,并将其与内部更改隔离开来。
答案 5 :(得分:1)
有些东西需要补充,虽然这已经有很长一段时间了。
当您的列表类型派生自List<T>
而不是Collection<T>
时,您无法实现Collection<T>
实现的受保护虚拟方法。
这意味着,如果对列表进行任何修改,派生类型将无法响应。这是因为List<T>
假设您在添加或删除项目时知道。能够响应通知是一种开销,因此List<T>
不提供它。
如果外部代码可以访问您的集合,您可能无法控制何时添加或删除项目。因此,Collection<T>
提供了一种了解列表修改时间的方法。
答案 6 :(得分:0)
我没有看到返回类似
之类的任何问题this.InternalData.Filter(crteria).ToList();
如果我返回了内部数据的断开副本或数据查询的分离结果 - 我可以安全地返回List<TItem>
而不暴露任何实现细节,并允许使用返回的数据方便。
但这取决于我期望的消费者类型 - 如果这是类似于数据网格的东西,我更愿意返回IEnumerable<TItem>
,在大多数情况下,这将是复制的项目列表 :)
答案 7 :(得分:-1)
Collection类实际上只是围绕其他集合的包装类,以隐藏其实现细节和其他功能。我认为这与面向对象语言中的属性隐藏编码模式有关。
我认为您不应该担心,但如果您真的想取悦代码分析工具,请执行以下操作:
//using System.Collections.ObjectModel;
Collection<MyClass> myCollection = new Collection<MyClass>(myList);