linq .Cast 或在ConvertAll中转换为列表

考虑您必须将List类型的mylist转换为List ,其中TBase子类

这些解决方案是否相同? 哪个有更好的表现,为什么? 我应该何时更喜欢使用第一个还是第二个?

 return mylist.Cast().ToList(); return mylist.ConvertAll(x => (Base)x); 

也许第二种解决方案可能会更好,因为mylist是直接转换的。

在第一个解决方案中,列表转换为IEnumerable,然后转换为列表,但我不确定。

TL; DR: ConvertAll进行1次内存分配,但在大多数情况下.Cast.ToList多次内存分配。

大多数LINQ扩展(如.Cast() )导致延迟执行IEnumerable ,无法转换为ICollection (无法获取结果的.Count )。

当结果可以转换为ICollection.ToList.ToArray可以只进行一次内存分配来复制元素,但是当它不能:

  • 最初为非空源分配4个元素缓冲区数组
  • 当需要更多元素的空间时,新数组的分配大小是前一个数组的两倍
  • 元素从旧数组复制到新数组,旧数组稍后由垃圾收集器释放。

更新

令人惊讶的是,这种差异似乎并不像我预期的那么重要:

 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().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}");