最近,我一直在研究函数返回集合的写法惯例。我想知道真正使用List<int>
的函数是应该返回List<int>
还是IList<int>
、ICollection<int>
或IEnumerable<int>
。我对性能进行了一些测试,结果让我感到非常惊讶。
static List<int> list = MakeList();
static IList<int> iList = MakeList();
static ICollection<int> iCollection = MakeList();
static IEnumerable<int> iEnumerable = MakeList();
public static TimeSpan Measure(Action f)
{
var stopWatch = new Stopwatch();
stopWatch.Start();
f();
stopWatch.Stop();
return stopWatch.Elapsed;
}
public static List<int> MakeList()
{
var list = new List<int>();
for (int i = 0; i < 100; ++i)
{
list.Add(i);
}
return list;
}
public static void Main()
{
var time1 = Measure(() => { // Measure time of enumerating List<int>
for (int i = 1000000; i > 0; i-- ) {
foreach (var item in list)
{
var x = item;
}
}
});
Console.WriteLine($"List<int> time: {time1}");
var time2 = Measure(() => { // IList<int>
for (int i = 1000000; i > 0; i-- ) {
foreach (var item in iList)
{
var x = item;
}
}
});
Console.WriteLine($"IList<int> time: {time2}");
var time3 = Measure(() => { // ICollection<int>
for (int i = 1000000; i > 0; i-- ) {
foreach (var item in iCollection)
{
var x = item;
}
}
});
Console.WriteLine($"ICollection<int> time: {time3}");
var time4 = Measure(() => { // IEnumerable<int>
for (int i = 1000000; i > 0; i-- ) {
foreach (var item in iEnumerable)
{
var x = item;
}
}
});
Console.WriteLine($"IEnumerable<int> time: {time4}");
}
输出:
List<int> time: 00:00:00.7976577
IList<int> time: 00:00:01.5599382
ICollection<int> time: 00:00:01.7323919
IEnumerable<int> time: 00:00:01.6075277
我尝试了不同的措施,包括让MakeList()
返回上述接口之一,但所有尝试都只证明返回一个List<int>
并将其作为List<int>
处理,速度大约是使用接口的两倍。
然而,包括这个答案在内的各种来源都声称永远不应该返回List<>
,而应该始终使用接口。
所以我的问题是:
- 为什么处理
List<int>
大约比接口快两倍? - 从函数中返回什么以及如何管理代码以获得更好的性能?
void Do(IEnumerable<int> source)
比void Do(List<int> source)
更好;返回值则相反:越具体,越好。List<int> Do()
比IEnumerable<int> Do()
更好。 - Dmitry BychenkoToList
制作一个。 - Eric Lippert