我应该喜欢IEnumerable 还是Arrays?
在我工作的许多项目中,每当我必须返回一个只读集合时,我使用IEnumerable
接口并使其类型特定如下:
Public ReadOnly Property GetValues() As IEnumerable(Of Integer) Get 'code to return the values' End Get End Property
大多数时候,我返回一个List,但是在一些函数和只读属性中,我返回一个数组,这个目的也可以通过扩展方法的礼貌来实现。
我的问题是我是否通过返回IEnumerable
而不是特定类型(例如: List
, HashSet
, Stack
或Array
s)来违反任何设计原则 ?
我通常也喜欢IEnumerable
。 主要的是问自己从方法返回的实际(甚至是最小)function(或者在方法参数的情况下传递给它)。
如果你需要做的就是对结果集进行枚举,那么IEnumerable
就是这样做的。 不多也不少。 这使您可以灵活地在某些情况下返回更多特定类型,如果需要,而不会破坏方法的足迹。
大卫的回答几乎涵盖了它; IEnumerable
是一般的最佳实践。 您可以通过查看大多数较新的框架方法来看到这一点。
我只是想补充一点,因为你在原始问题中指定了只读集合,你可以通过在返回IEnumerable
实例之前调用.ToList().AsReadOnly()
来强制执行此操作。 这将创建一个ReadOnlyCollection
的具体实例供您返回。 你的返回类型应该仍然是IEnumerable
,因为调用者不需要知道你是专门返回一个ReadOnlyCollection
,但这样你就可以防止调用者在脚中射击自己。
(如果没有这一步,调用者可以尝试将IEnumerable
从你的方法转换为List
。如果转换成功,那么如果这是你最初使用的那个,那么调用者就可以修改您在方法内操作的相同列表,可能会产生意外后果。)
我个人认为从API返回IEnumerable
强烈暗示实现可能使用延迟评估。
知道可以使用延迟评估对于调用者来说可能很重要,因为它意味着:
-
多次迭代结果(例如,获得计数然后访问数据)将导致评估不止一次。
-
迭代结果时,可以从API抛出exception:
例如
IEnumerable LazyEvaluatedApi() { } ... IEnumerable result = LazyEvaluatedApi(); ... foreach(MyObject item in result) // Exception from LazyEvaluatedApi may be thrown here. { }
如果您认为任何实现都不需要使用延迟评估(例如数据访问层API),我宁愿返回ICollection
或IList
。 除了让调用者访问Count
属性( ICollection
和IList
)和索引器(仅IList
)之外,您还清楚地表明不会进行延迟评估。
返回ICollection
或IList
,具体实现通常可能会返回List
。 如果列表是只读的很重要,那么返回List
这取决于你想做什么以及你打算“违反”。 这取决于:
- 如果您希望对象返回的集合可以被外部代码更改,则必须返回可修改的类型,即List <>,HashSet <> Stack <>或任何其他允许修改的类型
- 如果您希望“收集用户”只是迭代收集项而不修改收集本身,这是正确的选择
请记住,许多优秀的设计大师和原则更喜欢返回IEnumerable <> over modifiable-collection-ready
返回IEnumerable
很好。 虽然注意如果T是引用类型,则调用者可以修改对象。
您应该返回提供所需最低级别function的最常规类型。 在这种情况下,如果调用者只需要对数据进行交互,那么IEnumerable比List
, HashSet
或任何其他集合类型更合适。 这样,您的调用者仍然可以与方法的实现分离,并且您可以在将来更改方法实现,而不会中断调用者。
如果你不需要List
, HashSet
等提供的任何额外function,那么返回IEnumerable
就可以了,imo。
返回您认为最合适的任何类型; 如果你只需要遍历集合或者用它们做LINQ-y的东西,那么IEnumerable
在大多数情况下都是好的。
当您确实需要返回“更丰富”的类型时,请考虑返回一个接口而不是具体类型 – IList
而不是List
, ISet
而不是HashSet
等 – 以便您的实现如有必要,可以在不破坏任何调用代码的情况下进行更改。
这取决于。 如果您打算使用您的方法仅仅是枚举,那么IEnumerable是正确的选择。
如果索引查找等特定function很重要,那么您可能会更具体。
也就是说,您始终可以将IEnumerable包装在LINQ表达式中并将其用作任何内容。 如果返回的类型已经支持LINQ表达式中使用的function,那么聪明的编译器可以优化该表达式。
IEnumerable查询速度更快,因为它可以组合查询逻辑。 我通常使用数组来删除或更改值,因为我不想在我影响它时更改我的集合