考虑到您必须将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,然后转换为列表,但我不确定。
TL; DR:ConvertAll分配1个内存,但.Cast.ToList在大多数情况下分配多个。
大多数LINQ扩展(例如.Cast<T>())都会导致IEnumerable<T>无法强制执行的延期执行ICollection<T>(无法获得.Count结果的)。
当结果可以转换为时ICollection<T>,.ToList并且.ToArray只能进行一次内存分配来复制元素,但是不能这样做时:
更新资料
令人惊讶的是,这种差异似乎远没有我预期的那么大:
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)