如果返回类型是可枚举的,则返回列表是错误的

时间:2010-07-16 10:19:31

标签: c#

我经常遇到要从方法或属性返回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>();
}

这些例子非常简单,在这种情况下,我会从一开始就使用数组工作,但这只是为了解释这个问题。

8 个答案:

答案 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;
            }
        }
    }
}

这与以下列方式返回列表的版本有所不同,其中一些可能是不受欢迎的:

  1. 如果调用者多次枚举结果,则每次枚举时都会访问数据库并重新生成结果。

  2. 如果调用者使用Enumerable.Count扩展方法,则每次调用时都会访问数据库并重新生成结果。

  3. 在调用者实际枚举结果之前,不会执行该方法的主体。这意味着在调用者开始枚举之前不会抛出任何数据访问异常。这可能有问题的一种情况是,如果结果是作为WCF Web服务的结果直接返回的 - 在这种情况下,在WCF基础结构开始序列化结果之前不会抛出任何数据访问 - 以便用户代码在服务无法处理异常。

答案 7 :(得分:1)