我见过类似的问题,但我仍然对公共,内部和私人方法的返回类型感到迷茫。什么时候我的返回类型应该是Collection,什么时候应该是IEnumberable
X不要在公共API中使用弱类型集合。
表示集合项的所有返回值和参数的类型应该是确切的项类型,而不是其任何基类型(这仅适用于集合的公共成员)。
这让我完全糊涂了,暗示一切(公开)应该是Collection<T>
或ReadOnlyCollection<T>
。我的困惑在于我们何时可以使用List<T>
,例如
public Collection<string> DoThisThing(int i)
{
Collection<string> col = new Collection<string>();
List<string> myList = null;
if (i > 0)
myList = GetListOfString();
//col.AddRange(...); Can't AddRange as no method
//return myList; Can't do this as it expects return type of Collection<string>
//So, to ensure my return is Collection, I have to convert
foreach(var item in myList)
{
col.Add(item);
}
return col;
}
在上面的例子中,List是一个更好的选择是完全有效的,所以,如果我想使用AddRange
,我可以
但是,如果我选择选项1,那么我没有看到我何时实际使用ToList()
(除非必须在外部API返回List时),因为我的所有返回类型都是Collection<T>
,ReadOnlyCollection<T>
使用选项2似乎更明智但是,现在我迷失了,因为感觉好像我强迫它做一些不打算做的事情。
选项3是重复的
惠斯特我很欣赏它总是取决于具体情况,一般来说,是以下准确的
公开 - 应该非常精确,例如IEnumberable<string>
(注意,不是T
),而不是Collection<T>
/ Collection<string>
内部和私人 - 应该更加不可知,所以Collection<T>
更好(并且没有真正使用List,或者如果我必须使用,我将不得不将其投入使用Collection<T>
答案 0 :(得分:3)
编辑:最后添加了代码
您会混淆指南的几个不同层。
在您的内部代码中,您可以自由地执行您喜欢的操作,尽管有关于如何编写干净,可读且易于维护的代码的指南。不过,这些都是实施细节,它们隐藏在你的公共方法背后,所以你可以做你想做的事情(你可以自己动手)。
您编写的其他开发人员使用的工件就是其中之一:
现在,在这些情况下,你必须决定:
如果您选择有一个装配,则装配很好地定义边界。
有时,名称空间可能会被视为“内部边界”,而消费者将来会被视为自己。将代码组织到内部边界是一种很好的做法,但考虑到类已经定义了边界,所以不要过度使用它。通过内聚将命名空间中的10-20个类分组,并通过低耦合保持这些组之间没有相互依赖性。当我说 bridge 时,你无法真正理解我的意思......但是在一年左右的时间回来并再次阅读这个答案:)
如何使用程序集在这些地方定义:
现在使用所有这些工具,您可以设计一种名为 API 的东西。
有足够的空间来操纵和犯错误。因此,M $的优秀人员决定在默认程序集和命名空间(例如System.*
)中定义统一且一致的默认API设计方法。
您在问题中提到的是API设计人员的指南(您可能会或可能不会)。他们所说的只是:
如果您正在设计一个API,例如可访问程序集中的公共类的非内部方法,那么请使用我们的帮助并返回一个严格定义的值,尽可能具体。
因此,如果它是List<T>
,则不返回任何祖先,例如IEnumerable<T>
或其他任何东西。这允许程序集的使用者(其他开发人员)利用返回值的具体属性。
另一方面,您应该接受尽可能一般的价值观。如果您只是在方法中使用IEnumerable<T>
方法,则应该只需要:
public void MyMethod<T>(IEnumerable<T> e);
您方法的签名允许消费者传递许多不同的具体集合。您唯一需要的是它(来自)IEnumerable<T>
。
这些设计决策也与SOLID的许多重要OO原则相关。他们假设:
更准确地说,我会采用您的代码并尝试尽可能具体:
public List<string> DoThisThing(int i) // use list to return a more concrete type
{
List<string> list = new List<string>(); // you really want a *LIST* so why be abstract in your own code innards?
// List<string> myList = null; <-- superfluous
if (i > 0)
{
list = GetListOfString();
} // ALWAYS (!) use code blocks, see the book "Clean Code"
// this problem is solved: in your internal code don't be abstract if you don't need it
//col.AddRange(...); Can't AddRange as no method
return list; // Can't do this as it expects return type of Collection<string>
// you're done...
// So, to ensure my return is Collection, I have to convert
// foreach(var item in myList)
// {
// col.Add(item);
// }
// return col;
}
对所有这些背后的理论有所帮助:The SOLID principles。
您真正想要了解的是您需要抽象的方式和原因。当你真正掌握了这一点时,你也会理解什么时候你不需要它们,因此以合理的方式决定何时使用IEnumerable<T>
或List<string>
而不会对你的其余代码产生负面影响。在这种情况下,SOLID最重要的原则是OCP和DIP。
答案 1 :(得分:3)
你的问题似乎是在考虑错误的问题&#34;方向,从最初的MSDN报价开始:
X不要在公共API中使用弱类型集合。
表示集合项的所有返回值和参数的类型应该是确切的项类型,而不是其任何基类型(这仅适用于集合的公共成员)。
这是指集合的项,而不是集合类型。
本指南根本不涉及集合本身的类型。无论是返回可枚举,集合,列表还是任何其他集合类型,完全取决于您希望集合的行为方式以及您希望对存储的项目的集做出什么保证。
另一方面,您引用的指南表明您应该强烈输入收藏中存储的项。也就是说,如果您有string
值列表,请不要将集合项类型声明为object
(例如,将集合键入IEnumerable<object>
或IList<object>
),但请使用string
作为项目类型(例如IEnumerable<string>
,ICollection<string>
,IList<string>
)。