在C#中返回数组是不好的forms? 我应该返回List 吗?

我有一个返回可变数量元素的函数,我应该返回一个数组还是一个List? 一旦返回,“集合”的大小不会改变,即出于所有目的,集合是不可变的。 我想只返回一个数组,但有些人说不会从函数中返回变量大小的数组,因为它是“糟糕的forms”。 不知道为什么?

这需要与.NET 2.0兼容吗?

如果不需要,返回数组是不好的forms,特别是返回List

通常,您需要返回IEnumerableIList

如果您的用户只需要遍历每个元素, IEnumerable将提供此function。 它还允许您使用延迟执行来潜在地实现例程(现在或以后)。

如果您的用户需要按索引访问元素,请返回IList 。 这提供了数组的所有优点,但为您的实现提供了更大的灵活性。 您可以将它实现为数组,列表或其他实现IList集合,而不必转换/复制到数组。

一种观点我经常看到返回IListReadOnlyCollection 。 如果你有其中一个可以返回它们是可以的 – 两者都可以直接分配给IEnumerable (它们可以直接使用任何LINQ方法)。 需要注意的一点是, ReadOnlyCollection是一个非常轻量级的类型,可以包装任何IList

扩展里德的答案。

Eric Lippert在这个主题上写了一篇很棒的博文。 它可能是最详细的答案

正如你从这个主题的答案中无疑看到的那样,关于这个主题的意见范围很广。

总的来说,我的想法如下:

如果我返回一个大小恒定的列表,并且我不希望调用者能够修改我的数据(这是99%的时间),我将返回一个ReadOnlyCollection 。 这使我在客户端不变,而不必在创建新列表或数组时将数据的内存占用量增加一倍(或三倍,或其他)。

我不愿意说“你应该总是返回IEnumerableIEnumerable ”。 虽然这在某些情况下肯定是合适的(并且这些情况并不少), IEnumerable接口的轻量级特性极大地限制了您的function(没有基于索引的检索是最大的),并且在许多情况下,基础数据源是无论如何都要成为一个数组,即使它是List

返回IEnumerable的另一个危险来自于在该接口的上下文中简单地返回内部列表的懒惰实践。 这样做会让您通过将此快捷方式强制转换为更强大的集合类型来向您展示滥用此快捷方式的调用方法。 一个好的,防守的程序员不会这样做。

内存占用最少的是使用从List构建的ReadOnlyCollectionReadOnlyCollection仍会通过基于reflection的滥用暴露您的危险并捕获对可变列表的引用,但这有点像边缘情况。

如果在返回后没有更改集合的大小, IEnumerable将是我的选择,因为调用者可以立即使用列表中的LINQ扩展方法。

如果你需要一个数组,很容易从IList获取一个数组,但反之则不然。 因此返回IList是可取的。

除非您需要,否则返回IEnumerable

  1. access Count – 然后返回IReadOnlyCollection
  2. 修改元素和Count少于ArrayList.Capacity – 然后返回数组。
  3. 添加新元素 – 然后返回列表。
  4. 一些微观优化 – 做基准测试。 例如,foreach over array比列表更快

BenchmarkDotNet = v0.10.8,OS = Windows 10 Redstone 1(10.0.14393)处理器= Intel Core i5-2500K CPU 3.30GHz(Sandy Bridge),ProcessorCount = 4频率= 3233538 Hz,分辨率= 309.2588 ns,Timer = TSC [Host ]:Clr 4.0.30319.42000,64bit RyuJIT-v4.6.1648.0 Clr:Clr 4.0.30319.42000,64bit RyuJIT-v4.6.1648.0核心:.NET Core 4.6.25211.01,64bit RyuJIT

  Method | Job | Runtime | Mean | Error | StdDev | Min | Max | Median | Rank | Allocated | ---------- |----- |-------- |-----------:|-----------:|----------:|-----------:|-----------:|-----------:|-----:|----------:| TestList | Clr | Clr | 5,153.3 ns | 34.002 ns | 31.806 ns | 5,119.2 ns | 5,213.4 ns | 5,135.9 ns | 3 | 0 B | TestArray | Clr | Clr | 730.1 ns | 6.962 ns | 6.512 ns | 722.4 ns | 743.9 ns | 729.5 ns | 2 | 0 B | TestList | Core | Core | 5,188.4 ns | 102.816 ns | 96.174 ns | 5,070.3 ns | 5,342.6 ns | 5,185.6 ns | 3 | 0 B | TestArray | Core | Core | 709.0 ns | 6.126 ns | 5.730 ns | 700.8 ns | 718.6 ns | 708.1 ns | 1 | 0 B | 

码:

 [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn] [ClrJob, CoreJob] [HtmlExporter, MarkdownExporter] [MemoryDiagnoser] public class BenchmarkForEach { List testData = new List(); string[] testArray; public BenchmarkForEach() { for(int i=0;i<1000;i++) { testData.Add(i.ToString()); } testArray = testData.ToArray(); } [Benchmark] public int TestList() { var x = 0; foreach(var i in testData) { x += i.Length; } return x; } [Benchmark] public int TestArray() { var x = 0; foreach (var i in testArray) { x += i.Length; } return x; } }