31

在我从事的许多项目中,每当我必须返回只读集合时,我都会使用IEnumerable<T>接口并使其类型特定,如下所示:

Public ReadOnly Property GetValues() As IEnumerable(Of Integer)
    Get
        'code to return the values'
    End Get
End Property

大多数时候,我返回一个列表,但在某些函数和只读属性中,我返回一个数组,该数组也符合扩展方法的目的。

我的问题是我是否通过返回s 而不是特定类型(例如:、、s)违反了任何设计原则IEnumerable<T>List<T>HashSet<T>Stack<T>Array

4

8 回答 8

36

我一般也喜欢IEnumerable<T>。主要是问自己从方法返回的实际(甚至最小)功能是什么(或传递给它,在方法参数的情况下)。

如果您需要做的只是枚举一个结果集,那么IEnumerable<T>就这样做。不多也不少。如果需要,这使您可以灵活地在某些情况下返回更具体的类型,而不会破坏方法的占用空间。

于 2011-03-01T15:35:37.177 回答
18

大卫的回答几乎涵盖了它;IEnumerable<T>通常是最佳实践。您可以通过查看大多数较新的框架方法来了解这一点。

我只是想补充一点,因为您在原始问题中指定了只读集合,您可以通过在返回实例之前调用.ToList().AsReadOnly()您的实例来强制执行此操作。IEnumerable<T>这将创建一个具体实例ReadOnlyCollection<T>供您返回。您的返回类型应该仍然是IEnumerable<T>,因为调用者不需要知道您具体返回 a ReadOnlyCollection<T>,但是这样可以防止调用者在脚上开枪。

(如果没有这一步,调用者可能会尝试将IEnumerable<T>它从您的方法中获取到List<T>.您的方法,可能会产生意想不到的后果。)

于 2011-03-01T15:40:26.307 回答
16

就我个人而言,我认为IEnumerable<T>从 API 返回是一个强烈的暗示,即实现可能会使用惰性求值。

知道可以使用惰性求值对调用者来说可能很重要,因为这意味着:

  • 多次迭代结果(例如,获取计数然后访问数据)将导致多次执行评估。

  • 在对结果进行迭代时,可以从 API 抛出异常:

例如

IEnumerable<MyObject> LazyEvaluatedApi()
{
}

...

IEnumerable<MyObject> result = LazyEvaluatedApi();
... 
foreach(MyObject item in result) // Exception from LazyEvaluatedApi may be thrown here.
{
}

如果您认为任何实现都不需要使用惰性评估(例如数据访问层 API),我宁愿返回ICollection<T>or IList<T>。除了让调用者访问Count属性(ICollection<T>IList<T>)和索引器(IList<T>仅)之外,您还清楚地表明不会有惰性求值。

当返回你的ICollection<T>orIList<T>时,你的具体实现可能通常会返回一个List<T>. 如果列表是只读的很重要,则返回List<T>.AsReadOnly()

于 2011-03-01T17:10:57.347 回答
3

这取决于您想做什么以及您打算“违反”什么。这取决于:

  1. 如果您希望您的对象返回的集合可以被外部代码更改,您必须返回可修改的类型,即 List<>、HashSet<> Stack<> 或任何其他允许修改的类型
  2. 如果您希望您的“集合用户”只迭代集合项而不修改集合本身,那么这是正确的选择

请记住,许多优秀的设计大师和原则更喜欢返回 IEnumerable<> 而不是 modifiable-collection-ready

于 2011-03-01T15:39:51.470 回答
2

回来IEnumerable<T>就好了。尽管请注意,如果 T 是引用类型,则调用者可能会修改对象。

您应该返回提供所需功能的最低级别的最通用类型。List<T>在这种情况下,如果您的调用者只需要对数据进行交互,那么 IEnumerable 比HashSet<T>或任何其他集合类型更合适。这样,您的调用者与您的方法的实现保持分离,并且您将来可以自由地更改您的方法实现,而不会中断您的调用者。

于 2011-03-01T15:37:52.670 回答
0

如果您不需要List<T>, HashSet<T>etc 提供的任何额外功能,那么返回 anIEnumerable<T>就可以了,imo。

返回您认为最合适的任何类型;如果您只需要遍历集合或对它们进行 LINQ-y 处理,那么IEnumerable<T>在大多数情况下都很好。

当您确实需要返回“更丰富”的类型时,请考虑返回接口而不是具体类型 -IList<T>而不是List<T>,ISet<T>而不是HashSet<T>等 - 以便将来您的实现可以在必要时更改,而不会破坏任何调用代码。

于 2011-03-01T15:34:23.673 回答
0

这取决于。如果您打算使用您的方法仅仅是枚举,那么 IEnumerable 是正确的选择。

如果索引查找等特定功能很重要,那么您可以更具体。

也就是说,您始终可以将 IEnumerable 包装在 LINQ 表达式中并将其用作任何内容。如果您返回的类型已经支持 LINQ 表达式中使用的功能,那么聪明的编译器可以优化该表达式。

于 2011-03-01T15:37:55.387 回答
0

IEnumerable 查询速度更快,因为它可以结合查询逻辑。我通常使用数组来删除或更改值,因为我不想在影响它时更改我的集合

于 2011-03-01T15:53:01.643 回答