linq .Cast <>或在ConvertAll内强制转换为列表

2 c# linq casting

考虑到您必须将mylist类型转换List<T>List<Base>where T的子类Base

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

return mylist.Cast<Base>().ToList();

return mylist.ConvertAll(x => (Base)x);
Run Code Online (Sandbox Code Playgroud)

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

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

Sla*_*lai 5

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

大多数LINQ扩展(例如.Cast<T>())都会导致IEnumerable<T>无法强制执行的延期执行ICollection<T>(无法获得.Count结果的)。

当结果可以转换为时ICollection<T>.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
Run Code Online (Sandbox Code Playgroud)

所以,赛马

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}");
Run Code Online (Sandbox Code Playgroud)