C# 列表 .转换所有效率和开销



我最近了解了List的.全部转换扩展。我今天在工作中在代码中多次使用它,将我的大量对象列表转换为其他对象的列表。它似乎效果很好。但是,我不确定与仅迭代列表和转换对象相比,这有多高效或多快。做 .ConvertAll 使用任何特殊的东西来加快转换过程,还是只是一种无需设置循环即可转换列表的简略方法?

没有比直接找到源头更好的方法了,字面意思是:)

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

正如你所看到的,没有特别的魔法发生。它只是遍历列表,并通过您指定的转换器函数创建一个新项目。

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

  • 正如我所说,它更像是偶像,ConvertAll可能是 C#3.0 之前天的残余。无论如何,这都不是一个非常晦涩的方法,ConvertAll是一个非常清晰的描述,但坚持其他人知道的东西可能仍然更好,这是Select
  • 它适用于所有IEnumerable<T>,而ConvertAll仅适用于List<T>的实例。无论是数组、列表还是字典,Select都可以使用所有这些。
  • Select是懒惰的。在您迭代它之前,它不会执行任何操作。这意味着它返回一个IEnumerable<TOutput>,如果您实际上不需要列表,您可以通过调用或不调用 ToList() 将其转换为列表。或者,如果您只想转换并从一百万个项目的列表中检索前两个项目,您可以简单地执行source.Select(input => new Something(input.Name)).Take(2)

但是,如果您的问题纯粹是关于将整个列表转换为另一个列表的性能,那么ConvertAll可能会更快一些,因为它不如后跟ToListSelect通用(它知道列表具有大小,并且可以直接从底层数组中通过索引访问元素(。

使用 ILSPy 反编译:

public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter)
{
    if (converter == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
    }
    List<TOutput> list = new List<TOutput>(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. 返回新列表。

做 .全部转换使用任何特殊的东西来加速转换 过程还是只是转换列表的简略方法,而无需 必须设置循环?

它在转换方面没有任何特别的事情(它能做什么"特殊"的事情?它直接修改私有_items_size成员,因此在某些情况下可能会稍微快一点

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

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

以下是ConvertAll()的胆量:

List<TOutput> list = new List<TOutput>(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,因为它将进行惰性计算,这可能会避免大型列表的第二个副本,尤其是您需要在此过程中对项目进行任何过滤。

最新更新