I have a function which returns a variable number of elements, should I return an array or a List? The \"collection\'s\" size does not change once returned, ie for all purpo
One opinion I see pretty often a suggestion to return either IList<T>
or ReadOnlyCollection<T>
. It's OK to return these if you have one of these available - both can be assigned directly to an IEnumerable<T>
(they work directly with any LINQ methods). One thing to note is ReadOnlyCollection<T>
is a very lightweight type that can wrap any IList<T>
.
If the size of the collection is not to change after being returned IEnumerable<T>
would be my choice, since the caller could then use LINQ extension methods with the list immediately.
It's bad form to return arrays if not needed, and especially to return List<T>
.
Usually, you'll want to return IEnumerable<T>
or IList<T>
.
If your user is going to just need to run through each element, IEnumerable<T>
will provide this capability. It also allows you to potentially implement the routine (now or later) using deferred execution.
If your user needs to access elements by index, return IList<T>
. This provides all of the benefits of arrays, but gives you more flexibility in your implementation. You can implement it as an array, a list, or some other collection that implements IList<T>
, and you don't have to convert/copy to an array.
Return IEnumerable
unless you need:
IReadOnlyCollection
. BenchmarkDotNet=v0.10.8, OS=Windows 10 Redstone 1 (10.0.14393) Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4 Frequency=3233538 Hz, Resolution=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 Core : .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 |
Code:
[RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
[ClrJob, CoreJob]
[HtmlExporter, MarkdownExporter]
[MemoryDiagnoser]
public class BenchmarkForEach
{
List<string> testData = new List<string>();
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;
}
}
Expanding on Reed's answer.
Eric Lippert did a great blog post on this very subject. It's got probably the most detailed answer available
It is easy to get an array from an IList if an array is what you need, but the reverse is not true. So returning an IList is preferable.