C#List .ConvertAll效率和开销

我最近了解了List的.ConvertAll扩展。 我今天在代码中使用它几次,将我的对象的大型列表转换为其他对象的列表。 它似乎工作得很好。 但是,我不确定这与仅迭代列表和转换对象相比有多快或多快。 .ConvertAll是否会使用任何特殊的东西来加速转换过程,或者它只是一种简单的转换列表而无需设置循环的方法?

没有更好的方法来找出比直接去源,字面意思:)

http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs#dbcc8a668882c0db

正如你所看到的,没有特别的魔法在继续。 它只是迭代列表并通过您指定的转换器函数创建一个新项。

说实话,我不知道这种方法。 进行这种投影的更惯用的.NET方法是通过在IEnumerable上使用Select扩展方法,如下所示: source.Select(input => new Something(input.Name)) 。 这样做的好处有三个:

  • 正如我所说的那样,它更具有个性,转换器可能是前C#3.0天的残余。 无论如何,这不是一个非常神秘的方法, ConvertAll是一个非常清晰的描述,但坚持其他人所知道的可能仍然更好,这就是Select
  • 它在所有 IEnumerable可用,而ConvertAll仅适用于List实例。 无论是数组,列表还是字典, Select都无关紧要。
  • Select是懒惰的。 在迭代之前它不会做任何事情。 这意味着它返回一个IEnumerable ,如果你实际上不需要列表,你可以通过调用ToList()来转换为列表。 或者,如果您只想转换并检索一百万个项目列表中的前两个项目,您只需执行source.Select(input => new Something(input.Name)).Take(2)

但是如果你的问题纯粹是关于将整个列表转换到另一个列表的性能,那么ConvertAll可能会更快一些,因为它不像Select后跟ToList (它知道列表有一个大小并且可以直接访问例如,来自底层数组的索引元素)。

使用ILSPy进行反编译:

 public List ConvertAll(Converter converter) { if (converter == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter); } List list = new List(this._size); for (int i = 0; i < this._size; i++) { list._items[i] = converter(this._items[i]); } list._size = this._size; return list; } 
  1. 创建一个新列表。
  2. 通过迭代当前实例来填充新列表,执行指定的委托。
  3. 返回新列表。

.ConvertAll是否会使用任何特殊的东西来加速转换过程,或者它只是一种简单的转换列表而无需设置循环的方法?

它在转换方面没有做任何特殊的事情(它可以做什么“特殊”的事情?)它直接修改私有_items_size成员,所以在某些情况下它可能会更快。

像往常一样,如果解决方案使您的工作效率更高,代码更容易阅读等,请使用它直到分析显示出一个令人信服的性能原因, 使用它。

这是你描述它的第二种方式 – 基本上是一种没有设置循环的简单方法。

这是ConvertAll()的胆量:

 List list = new List(this._size); for (int index = 0; index < this._size; ++index) list._items[index] = converter(this._items[index]); list._size = this._size; return list; 

其中TOutput是您要转换的任何类型, converter是一个委托,指示将进行转换的方法。

因此,它遍历您传入的List ,通过您指定的方法运行每个元素,然后返回指定类型的新List

为了在场景中精确计时,您需要自己测量。

不要指望任何奇迹 – 它必须是O(n)操作,因为每个元素都需要转换并添加到目标列表中。

考虑使用Enumerable.Select因为它会进行延迟评估,可能允许避免大型列表的第二个副本,尤其是您需要在整个过程中对项目进行任何过滤。