8

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

4

4 回答 4

13

从字面上看,没有比直接访问源更好的方法了:)

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

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

老实说,我不知道这种方法。进行这种投影的更惯用的 .NET 方法是通过使用Select扩展方法,IEnumerable<T>如下所示: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可能会更快一些,因为它比 aSelect后跟 a更通用ToList(它知道列表具有大小并且可以通过索引直接访问元素例如,来自底层数组)。

于 2014-05-03T01:14:07.043 回答
4

使用 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. 返回新列表。

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

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

像往常一样,如果该解决方案使您更有效率,代码更易于阅读等,请使用它,直到分析显示一个令人信服的性能理由使用它。

于 2014-05-03T01:07:30.193 回答
1

这是您描述它的第二种方式-基本上是一种不设置循环的速记方式。

这是胆量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指定类型的新元素。

于 2014-05-03T01:07:59.007 回答
0

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

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

考虑Enumerable.Select改用它,因为它会进行惰性评估,这可能会避免大列表的第二个副本,尤其是您需要在此过程中对项目进行任何过滤。

于 2014-05-03T01:08:32.560 回答