2011-03-01 68 views
22

在许多项目中,我的工作,每当我必须返回一个只读集合,我用的是IEnumerable<T>接口,并使其键入特定像这样:我应该支持IEnumerable <T>或Arrays?

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

大部分的时间,我返回一个列表,但在一些函数和只读属性我返回一个数组,它也可以通过扩展方法提供的好处。

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

+1

http://stackoverflow.com/questions/3433284/ienumerablet-vs-t – Alex 2011-03-01 15:36:48

回答

28

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

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

+8

这是没有风险的,虽然,当与数组相比,鉴于'IEnumerable'可能会隐藏一些执行以便访问元素,而'[]'永远不会?即。延期执行的潜在副作用是否会超过某些好处? – 2013-04-29 06:28:24

+2

数组在其索引器中也有代码 – user1496062 2013-11-26 12:24:47

0

如果你不需要List<T>,HashSet<T>等提供的任何额外功能,那么返回IEnumerable<T>是好的,imo。

返回你觉得最合适的类型;如果你只需要遍历集合或者LINQ-Y的东西,那么IEnumerable<T>在大多数情况下都是很好的。

当你需要返回“富”的类型,然后考虑返回一个接口,而不是一个具体类型 - IList<T>而不是List<T>ISet<T>而非HashSet<T>等 - 这样您的实施可以在未来,如果需要更改,不会破坏任何调用代码。

2

返回IEnumerable<T>很好。尽管注意如果T是一个引用类型,调用者可能会修改该对象。

您应该返回提供所需功能的最低级别的最一般类型。在这种情况下,如果您的调用者只是需要对数据进行交互,那么IEnumerable比List<T>HashSet<T>或任何其他集合类型更合适。通过这种方式,您的调用者与您的方法的实现保持解耦,并且您可以随时在未中断调用者的情况下更改您的方法实现。

0

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

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

这就是说,你总是可以将你的IEnumerable包装在一个LINQ表达式中,并将它用作任何东西。如果您的返回类型已经支持您的LINQ表达式中使用的功能,那么聪明的编译器可以优化该表达式。

+4

您的最后一段没有意义... – Domenic 2011-03-01 15:41:37

+1

您可以随时在枚举上调用ToList或ToArray LINQ扩展。如果源枚举本身是一个列表/数组,并且用法的语义允许它(该列表未被修改),那么聪明的编译器可以绕过列表或数组生成。 – Holstebroe 2011-03-01 17:18:54

3

这取决于你想要做什么以及你打算“违反”什么。 这取决于来自:

  1. 如果你希望你的收藏集的对象返回可以通过外部的代码被改变时,你必须返回修改类型又名名单<>,HashSet的<>堆栈<>或任何其他其允许修改
  2. 如果你希望你的“收集用户”只是在收集项目,但不修改集合本身迭代,这是正确的选择考虑

采取许多优秀的设计大师和原则,更愿意返回的IEnumerable <> over modifiable-collection-ready

17

David的回答几乎涵盖了它;一般而言,最佳做法是使用IEnumerable<T>。您可以通过查看大多数较新的框架方法来了解这一点。

我只想补充一点,既然您在原始问题中指定了只读集合,您可以在返回之前通过在您的IEnumerable<T>实例上调用.ToList().AsReadOnly()来强制执行此操作。这将创建一个ReadOnlyCollection<T>的具体实例供您返回。您的退货类型仍然应该是IEnumerable<T>,因为来电者不需要知道您具体返回了ReadOnlyCollection<T>,但这样可以防止来电者在脚下自己拍摄。 (如果没有这一步,调用者可能会尝试将它从你的方法中获得的IEnumerable<T>转换为List<T>。如果转换成功,如果这是你最初使用的那个,调用者可以修改相同的列表是在您的方法内部运行的,可能会带来意想不到的后果。)

0

IEnumerable查询的速度更快,因为它可以组合查询逻辑。我通常使用数组删除或更改值,因为我不想改变我的收藏,而我影响它

12

我个人认为,从API返回IEnumerable<T>是一个强烈暗示的实施可以使用懒人评估。

明知惰性求可用于可能是重要的呼叫者,因为它意味着:

  • 遍历结果多于一次(例如,以获得一个计数然后访问数据)将导致在评估过程中不止一次完成。

例如:

  • 例外可以从API而迭代的结果被抛出

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

    如果你不认为任何实现将需要使用懒评价(如数据访问层API),我宁愿回到ICollection<T>IList<T>。除了给访问者一个Count属性(ICollection<T>IList<T>)和一个索引器(仅限于IList<T>)之外,您还清楚地表明不会有懒惰的评估。

    当您返回您的ICollection<T>IList<T>时,您的具体实现通常可能会返回List<T>。如果重要的是清单是只读的,那么返回List<T>.AsReadOnly()

  • +0

    很好的答案。在这种情况下,我从来不明白为什么EF导航属性使用[ICollection ](https://msdn.microsoft.com/en-us/library/92t2ye13(v = vs.110).aspx)。 – Chalky 2016-10-30 20:45:27