我经常遇到要从方法或属性返回Enumerable<T>
的情况。要构建返回的Enumerable<T>
,我使用List<T>
- 实例。填写完列表后,我将返回列表。
我一直认为这就够了。但是,调用者可能会将生成的Enumerable<T>
转换回List<T>
并开始进一步使用它。如果稍后我更改了我的方法的实现,则调用者的代码将失败。为了避免这种情况,我可以返回list.ToArray或制作一个只读列表,然后再将其返回给调用者。但对我来说,这似乎是一个很大的矫枉过正。你觉得怎么样?
请注意,我永远不会返回内部使用的列表,以便调用者可以更改我的对象内部状态。问题只是关于一个短暂的生活清单,这个清单是临时建立的,用于保存返回值。
IEnumerable<string> GetAList() {
List<string> aList = new List<string>();
aList.Add("a");
aList.Add("b");
return aList;
}
IEnumerable<string> GetAList() {
List<string> aList = new List<string>();
aList.Add("a");
aList.Add("b");
return aList.ToArray<string>();
}
这些例子非常简单,在这种情况下,我会从一开始就使用数组工作,但这只是为了解释这个问题。
答案 0 :(得分:15)
答案 1 :(得分:14)
不,这很好。
这是“多态”的一个例子。因为方法的调用者只对IEnumerable<string>
感兴趣,所以只要方法派生自IEnumerable<string>
接口,该方法的内部工作方式就可以自由返回它喜欢的类。
如果来电者选择IEnumerable<string>
并投出List<string>
,那么他们已经违反了合同,该合同只会声明将返回'IEnumerable<string>
。
答案 2 :(得分:6)
答案 3 :(得分:4)
我认为这是一个完整的答案,而不仅仅是评论。
如其他人所说,如果你遵守合同,那么其他人不应该做出更多的假设。但是,有些人可能会。如果你随后改变它,它们就不可避免地会因为它破坏而责备你(如果他们不能理解它们,那么首先他们不可能很快发现它们。)
当发生这种情况时,您有两种可能性。你告诉他们他们的问题,然后让他们修复它或你改变你的代码来支持他们的。 正确的解决方案是第一个。但是,在商业中这可能是不可行的。如果客户说你的老板说你需要保持高兴,你可能会被告知你的代码需要改变以保持客户满意并保持他的业务。
所以从这个角度来看,虽然这不是你的责任,但它可能成为你的问题。
如果您的代码很容易重构,以返回承诺的确切类型,那么我会这样做。现在可能很少有工作来节省大量工作,理论上没有人会注意到你的改变。
答案 4 :(得分:3)
答案 5 :(得分:2)
当您向代码用户返回IEnumerable<T>
合同时,表明无论您返回什么内容都会实现IEnumerable<T>
而不再执行。如果某人基于此做出假设,那么他们的编程实践就很糟糕,所以不要担心。在这种情况下,本地构建的List<T>
很好。
答案 6 :(得分:2)
扩大对Marcelo Cantos的回答。
这里的一些人建议使用yield来保证调用者只能获得IEnumerable<T>
合同承诺的最小实现。
然而,有一些潜在的缺点需要考虑,因此我认为不应该建议在所有情况下将其作为一揽子解决方案。
例如,考虑使用yield而不是返回列表的以下数据访问方法:
public IEnumerable<SomeType> YieldMethod(...)
{
using(IDbConnection connection = )
{
...
using (IDataReader reader = ...)
{
while(reader.Read())
{
SomeType someType = ...
...
yield return someType;
}
}
}
}
这与以下列方式返回列表的版本有所不同,其中一些可能是不受欢迎的:
如果调用者多次枚举结果,则每次枚举时都会访问数据库并重新生成结果。
如果调用者使用Enumerable.Count
扩展方法,则每次调用时都会访问数据库并重新生成结果。
在调用者实际枚举结果之前,不会执行该方法的主体。这意味着在调用者开始枚举之前不会抛出任何数据访问异常。这可能有问题的一种情况是,如果结果是作为WCF Web服务的结果直接返回的 - 在这种情况下,在WCF基础结构开始序列化结果之前不会抛出任何数据访问 - 以便用户代码在服务无法处理异常。
答案 7 :(得分:1)