【问题标题】:OK to return an internal List<T> as an IEnumerable<T> or ICollection<T>? [duplicate]可以将内部 List<T> 作为 IEnumerable<T> 或 ICollection<T> 返回吗? [复制]
【发布时间】:2012-04-28 06:45:10
【问题描述】:

可能重复:
Collection<T> versus List<T> what should you use on your interfaces?

考虑这个方法——返回的变量myVarList&lt;T&gt;,但是方法MyMethod()的返回类型是IEnumerable&lt;T&gt;

public IEnumerable<T> MyMethod(string stuff)
{
     var myVar = new List<T>();
     //do stuff

     return myVar;
}

基本上,我想知道的是,如果将myVar 作为不同的类型返回是否可以。

具体来说,就我的情况而言,“做事”通过DataRow 并将DataRow 中的项目分配给对象列表。我在其他地方也有ICollectionIList 返回类型的类似情况。

我想返回IEnumerableICollection 的原因是我返回的次数不会超出需要。但同时,这允许调用者在需要时将返回值转换为List

但是,我的 return 语句返回的是 List,而不是方法的返回类型,这似乎很奇怪。这是正常的做法吗?这样做有什么问题吗?

澄清(回应骗子):

澄清一下,我很好奇的是,我在正文中的 return 语句是否可以返回 List&lt;T&gt;,但方法标头的返回类型为 IEnumerable,或者可能是 @987654338 @、Collection 等...与主体返回语句不同的东西。

【问题讨论】:

  • 调用者不应该从 IEnumerable 转换为 List。它返回接口的原因是,是否存在底层列表、可观察集合、简单数组甚至通过 yield return 的列表都无关紧要。
  • 我认为这两个建议的骗子比这个问题更接近对方的骗子,尽管它们都密切相关。
  • @ChrisCharabaruk 我同意这个问题不是重复的,我已经对其进行了重新设计,希望能更好地传达我认为使它独一无二的东西。我也投票决定重新提出问题。

标签: c# collections return return-type


【解决方案1】:

没有错。

实际上返回具体类是满足“返回接口”要求的唯一方法。

如果您让特定类的知识潜入调用代码 (List&lt;T&gt; r = (List&lt;T&gt;)MyMethod("ff")),可能会产生不利影响。但是,如果您将结果视为接口(如您所愿),那就没问题了。

【讨论】:

    【解决方案2】:

    返回IEnumerable&lt;T&gt; 类型的原因是调用者无法修改列表(无需将其转换回列表)。如果您注意到所有(大多数?)扩展方法都采用 IEnumerable&lt;T&gt; 参数,那么您就知道扩展方法不会修改您的列表。

    其次,List&lt;T&gt; 继承自 IEnumerable&lt;T&gt;

    编辑: 正如托马斯在评论中解释的那样,IEnumerable&lt;T&gt; 可以被转换回List&lt;T&gt; 并由调用者修改。如果您的主要目标是使其成为只读,您可以将列表返回为myVar.AsReadOnly()。但是类型是ReadOnlyCollection&lt;T&gt;

    【讨论】:

    • 不确定防止修改是否是主要原因
    • 它不会阻止调用者修改列表:调用代码仍然可以将结果转换为 List。但是,这样做意味着对实现做出未记录的假设,因此如果实现发生变化,它可能会中断。
    • 并且返回 ReadOnlyCollection&lt;T&gt; 保留返回值的数组语义。也就是说,可以通过其整数索引检索项目。并且无需迭代集合即可获得集合中项目的计数。
    【解决方案3】:

    这不仅没有任何问题,而且实际上是一种很好的做法:只公开绝对必要的内容。这样一来,调用者就不能依赖该方法将返回 List&lt;T&gt; 的事实,因此如果出于某种原因您需要更改实现以返回其他内容,您不会违反合同。但是,如果调用代码(错误地)假设了方法实际返回的内容,调用代码可能会中断。

    【讨论】:

    • "....如果需要,调用者可以将其转换为列表"听起来这已经是预期的,还是好的做法?
    • @TimSchmelter,好吧,如果调用者应该知道该方法返回一个List,那么返回类型应该是List,当然...一个好的实践问题,只是一个常识问题;)
    • 那么验证一下,我的return语句返回一个List,但是方法的返回类型是IEnumerable,这样可以吗?
    • @Tonnie,是的,这很好,除非您需要调用者知道这是一个列表
    • Code Analysis (and FxCop) 建议在返回具体的泛型集合时,应返回以下内容之一:Collection&lt;T&gt;ReadOnlyCollection&lt;T&gt;KeyedCollection&lt;T&gt;。并且 List&lt;T&gt; 应该被返回。在 OP 的情况下,返回 IList&lt;T&gt; 将允许对返回的集合进行索引。返回List&lt;T&gt;.AsReadOnly() 将返回ReadOnlyCollection&lt;T&gt;(它还允许通过其整数索引检索项目)。
    【解决方案4】:

    我认为这不是最优的,因为它允许调用者转换为 List&lt;T&gt;,这依赖于实现细节。我会添加某种掩码,例如Select(x=&gt;x)

    动态类型的调用者甚至可能不会注意到您将列表转换为IEnumerable&lt;T&gt;。对他来说就是List&lt;T&gt;

    返回一个内部永久的List&lt;T&gt; 作为IEnumerable&lt;T&gt; 是非常错误的,因为它允许调用者改变你的类的内部状态。但是,由于您在每次调用时都返回一个新的 List&lt;T&gt; 实例,它不会在其他任何地方使用,因此此参数不适用于您的示例。

    【讨论】:

    • 如果你想这样做,你还不如使用迭代器;)
    • 将列表包装在一些东西中会增加无用的运行时包袱。但也许有人可以在 100 次中返回 List&lt;T&gt; 99 次,并且偶尔只返回一个包装器?这会破坏转换为 List 的代码,但不会严重影响性能。
    • @supercat 因为对于 99% 的代码来说,这个小开销并不重要,如果我的分析表明它很重要,我只会担心它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 2019-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多