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