linq .Cast或在ConvertAll内强制转换为列表 [英] linq .Cast<> or cast inside ConvertAll for a list
问题描述
考虑到您必须将类型为List<T>
的mylist
转换为List<Base>
,其中T
是Base
的子类
Consider you have to convert mylist
of type List<T>
to List<Base>
where T
is subclass of Base
这些解决方案是否相同?哪个表现更好,为什么? 我什么时候应该使用第一个或第二个?
Are these solutions the same? Which has better performances, and why? When should I prefer using the first or the second?
return mylist.Cast<Base>().ToList();
return mylist.ConvertAll(x => (Base)x);
第二种解决方案可能会更好,因为mylist是直接转换的.
Maybe the second solution could be better because mylist is converted directly.
在第一个解决方案中,列表被转换为IEnumerable,然后转换为列表,但我不确定.
In the first solution the list is converted to IEnumerable, then to list, but I'm not sure.
推荐答案
TL; DR:ConvertAll
分配1个内存,但是.Cast.ToList
在大多数情况下不止一个.
TL;DR: ConvertAll
makes 1 memory allocation, but .Cast.ToList
more than one in most cases.
大多数LINQ扩展(例如.Cast<T>()
)导致延期执行IEnumerable<T>
,而该延期执行不能转换为ICollection<T>
(无法获得结果的.Count
).
Most LINQ extensions (like .Cast<T>()
) result in a deferred execution IEnumerable<T>
that can't be cast to ICollection<T>
(can't get the .Count
of the result).
当结果可以转换为ICollection<T>
时, .ToList
和 .ToArray
只能分配一个内存来复制元素,但是在不能分配内存时:
When the result can be cast to ICollection<T>
, .ToList
and .ToArray
can make just one memory allocation to copy the elements, but when it can't:
- 最初为非空源分配了4个元素的缓冲区数组
- 当需要更多元素的空间时,将以前一个数组大小的两倍分配新数组
- 将元素从旧数组复制到新数组,然后由垃圾回收器释放旧数组.
更新
令人惊讶的是,这种差异似乎远没有我预期的那么重要:
Surprisingly, the difference doesn't seem nowhere near as significant as I expected:
method elapsed ratio count
Cast.ToList 00:00:14.4487329 1.3719890831991 123456789
ConvertAll 00:00:10.5312302 0.728868773261865
Cast.ToList 00:00:01.4959734 1.50233158227713 12345678
ConvertAll 00:00:00.9957678 0.665632016125407
Cast.ToList 00:00:00.1252968 2.45948743599897 1234567
ConvertAll 00:00:00.0509442 0.40658878161491
Cast.ToList 00:00:00.0082611 3.99145006839945 123456
ConvertAll 00:00:00.0020697 0.250535515380002
Cast.ToList 00:00:00.0008097 0.620558719826417 12345
ConvertAll 00:00:00.0013049 1.61145104895105
Cast.ToList 00:00:00.0001812 0.193207547169811 1234
ConvertAll 00:00:00.0009378 5.17578125
Cast.ToList 00:00:00.0001433 0.149501661129568 123
ConvertAll 00:00:00.0009587 6.68888888888889
所以,为马奔腾!
int c = 123; var L = Enumerable.Range(0, c).ToList();
GC.Collect(); var sw1 = Stopwatch.StartNew(); L.Cast<object>().ToList(); sw1.Stop();
GC.Collect(); var sw2 = Stopwatch.StartNew(); L.ConvertAll(i => (object)i); sw2.Stop();
MessageBox.Show($"Cast.ToList\t{sw1.Elapsed}\t{(double)sw1.ElapsedTicks / sw2.ElapsedTicks}\n" +
$"ConvertAll \t{sw2.Elapsed}\t{(double)sw2.ElapsedTicks / sw1.ElapsedTicks}");
这篇关于linq .Cast或在ConvertAll内强制转换为列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!