【问题标题】:linq .Cast<> or cast inside ConvertAll for a listlinq .Cast<> 或在 ConvertAll 中转换为列表
【发布时间】:2017-02-23 23:08:46
【问题描述】:

考虑您必须将List&lt;T&gt; 类型的mylist 转换为List&lt;Base&gt;,其中TBase 的子类

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

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

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

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

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

【问题讨论】:

  • 你对“更好”的定义是什么?
  • @DavidG 我的意思是性能更好,我会编辑问题
  • 在这种情况下,去阅读这个ericlippert.com/2012/12/17/performance-rant
  • @DavidG 我真的很喜欢第一部分 :o)
  • @Scancy 你读过 Eric Lippert 的博客吗?谁是你赛马的赢家?

标签: c# linq casting


【解决方案1】:

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

大多数 LINQ 扩展(如 .Cast&lt;T&gt;())会导致延迟执行 IEnumerable&lt;T&gt; 无法转换为 ICollection&lt;T&gt;(无法获得结果的 .Count)。

当结果可以转换为ICollection&lt;T&gt;时,.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<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}");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 2021-06-21
    • 2018-03-15
    • 1970-01-01
    相关资源
    最近更新 更多