问题描述
我找到了Google C#样式指南以及这些要点
对于输入,应使用限制性最强的集合类型,例如当输入应为不可变时,例如IReadOnlyCollection / IReadOnlyList / IEnumerable作为方法的输入。
对于输出,如果将返回的容器的所有权传递给所有者,则首选IList而不是IEnumerable。如果不转让所有权,请选择限制性最强的选项。
关于我无法理解的收藏集。
-
a)关于最具限制性的收藏是什么意思?我想它们隐含了如下的接口继承:
IReadOnlyList<T>
继承了IReadOnlyCollection<T>
,后者继承了IEnumerable<T>
。所以IEnumerable
是限制性最强的一个?b)最重要的是为什么这很重要?任何人都可以随时将上述集合投放到
List<T>
并更改内容-我认为没有理由选择“最严格的选择” -
它们是否只需要返回接口?这是否意味着我必须始终将我的
List<T>
强制转换为IEnumerable<T>
-是误导或影响性能吗? -
他们提到转让所有权。什么时候我想转让所有权,什么时候我不想这样做?如果我为什么这样做
解决方法
1a)限制性最强的集合的确是IEnumerable<T>
,因为它提供的操作最少。最好接受限制最大的集合作为输入,因为它为呼叫者提供了最大的自由度。由于您知道集合具有较少的自由度,例如不可变集合的原因更容易推断。
1b)这是不正确的。 IEnumerable<T>
不一定是列表。它也可能是一个自定义集合,恰好实现了接口以及许多其他事情。您的演员表很有可能失败。
2)如果返回一个集合,但您仍然拥有它,则希望限制调用方仅对集合执行所需的操作。如果要继续检查集合的内容,则不应允许调用者对其进行写入。另外,您可以从返回类型为List<T>
的方法中返回IEnumerable<T>
,而无需进行强制转换。
3)基本上,转移所有权意味着从现在开始,您已经将集合返回给的调用方对此负责。您(被呼叫者)不会在乎谁会添加到集合中,也不会承担重新分配它的任何责任。在这种情况下,应该通过将集合作为其实际类型公开而不是限制性更强的抽象,来赋予调用者完全访问权限。
,如果您传递所有权-您基本上就是一个工厂,它将创建一些集合,其生命周期由调用者控制:
public class A : IA
{
public IList<int> Create(){return new List<int>();}
}
例如,如果您不传递所有权,则可以自由地为多个调用者提供相同的集合,因此可以以几乎为零的成本进行缓存:
public class A : IA
{
private List<int> _someCollection = new List<int>();
public IEnumerable<int> Create(){return _someCollection; }
}
如果您改为传递IList,则没有人可以保护此IList免受工厂外的意外修改,这将导致缓存损坏。
关于界面...
Google鼓励您使用接口而不是具体类型,因为存在诸如 Unit Testing 和良好的重构实践之类的东西,如果您使用具体类型,这实际上是无法管理的。
例如,如果我要测试这种具体方法调用中没有更多RAM的情况,或者说第6个元素后无法枚举集合,那么将如何测试呢?或者我希望我的收藏集在检索前三个元素时表现最佳?我只是实现接口:
public class MyOverflowAndPerformantList : IList<int>{/*some bad implementation here*/}
在使用具体类型的情况下,您实际上会炸毁内存,或者使用一些怪异的技巧来解决问题,因此这是不切实际的。