【问题标题】:C# List<T> vs IEnumerable<T> performance questionC# List<T> vs IEnumerable<T> 性能问题
【发布时间】:2009-07-31 09:15:02
【问题描述】:

你好,假设这两种方法:

private List<IObjectProvider> GetProviderForType(Type type)
        {
            List<IObjectProvider> returnValue = new List<IObjectProvider>();

            foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
            {
                if ((provider.Key.IsAssignableFrom(type) ||
                    type.IsAssignableFrom(provider.Key)) &&
                    provider.Value.SupportsType(type))
                {
                    returnValue.Add(provider.Value);
                }
            }
            return returnValue;
        }

private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
        {
            foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
                if ((provider.Key.IsAssignableFrom(type) ||
                    type.IsAssignableFrom(provider.Key)) &&
                    provider.Value.SupportsType(type))

                    yield return provider.Value;              
        }

哪个更快?当我查看第一种方法时,我看到内存是为 List 分配的,在我看来它是不需要的。 IEnumerable 方法对我来说似乎更快。

例如,假设您调用

int a = GetProviderForType(myType).Count;
int b = GetProviderForType1(myType).Count();

现在,另一个问题是,上面这两个之间有性能差异吗?

你怎么看?

【问题讨论】:

  • 所有“哪个更快?”的答案问题是一样的:尝试两种方式。拿出秒表。那你就知道了。

标签: c# performance list ienumerable


【解决方案1】:

在这种特殊情况下,使用IEnumerable&lt;T&gt; 表单会更有效,因为您只需要知道计数。如果不需要,存储数据、调整缓冲区大小等是没有意义的。

如果您出于任何原因需要再次使用结果,List&lt;T&gt; 表单会更有效。

注意Count() 扩展方法和Count 属性对于List&lt;T&gt; 都是有效的,因为Count() 的实现会检查目标序列是否实现ICollection&lt;T&gt; 并使用Count 属性如果是这样。

另一个更高效的选项(虽然只是)是调用Count的重载,它需要一个委托:

private int GetProviderCount(Type type)
{
  return _objectProviders.Count(provider =>
      (provider.Key.IsAssignableFrom(type) 
       || type.IsAssignableFrom(provider.Key))
      && provider.Value.SupportsType(type));
}

这将避免Where 和Select 子句引起的额外间接级别。

(正如 Marc 所说,对于少量数据,无论如何性能差异可能可以忽略不计。)

【讨论】:

  • 我认为在这种情况下你应该返回一个 int
  • 你所有的答案都对我很有帮助,但我认为这个答案已经提到了一切,所以我接受了。谢谢!
  • 对不起乔恩。假设非空Books 是IEnumerable&lt;Book&gt; 或List&lt;Book&gt; 类型,我只需要阅读并且从不修改(添加、删除、插入等)Books。哪个效率更高,Books[0].Author 还是 Books.FirstOrDefault().Author?
  • @MoneyOrientedProgrammer:如果是IEnumerable&lt;Book&gt;,Books[0].Author 将无法编译。对于List&lt;Book&gt;,我会使用Books[0].Author,因为它可能更具可读性。使用FirstOrDefault() 肯定很奇怪,但是当它返回null 时不处理这种情况 - 取消引用以获取作者会引发异常。
  • 是的。当然,初步假设是Books[0].Author 用于非空 Books 类型为List&lt;Books&gt; 和Books.FirstOrDefault().Author 用于非空 Books 类型为@987654350 @。没有调整大小是附加的假设。
【解决方案2】:

这个问题的一个重要部分是“数据有多大”?多少行...

对于少量数据,列表很好 - 分配足够大的列表所需的时间可以忽略不计,并且不会多次调整大小(没有,如果你能提前告诉它有多大)。

但是,这不适用于庞大的数据量;您的提供商似乎不太可能支持数千个接口,所以我不会说采用这种模式是必要 - 但它不会造成太大伤害。

当然,你也可以使用 LINQ:

return from provider in _objectProviders
       where provider.Key.IsAssignableFrom(type) ...
       select provider.Value;

这也是隐蔽的延迟yield 方法...

【讨论】:

    【解决方案3】:

    此类问题的准确答案可能会因许多因素而异,并且可能会随着 CLR 的发展而进一步变化。唯一可以确定的方法是对其进行测量 - 请记住,如果与将出现的操作相比差异很小,那么您应该选择最易读、可维护的编写方式。

    关于这一点,您可能还想尝试:

    private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
    {
        return _objectProviders.Where(provider => 
                      provider.Key.IsAssignableFrom(type) ||
                      type.IsAssignableFrom(provider.Key)) &&
                      provider.Value.SupportsType(type))
                               .Select(p => p.Value);
    }
    

    如果您想将结果“快照”到列表中,您还可以通过返回 IEnumerable&lt;T&gt; 然后使用 ToList 扩展方法来给自己很大的灵活性。如果您需要多次检查,这将避免重复评估代码以生成列表。

    【讨论】:

      【解决方案4】:

      IEnumerable 和 IList 的主要区别:

      IEnumerable: 实现 MoveNext、Reset、获取当前方法并返回 IEnumerator 类型进行迭代 通过记录。

      IList : 公开 IEnumerable 接口,它也是可以通过索引访问的非通用对象的集合,因此 IEnumerable+ICollection(数据操作)和添加、删除、插入(在特定索引处)很有用由 IList 实现的方法。

      在我的意见中查看您的代码后,IEnumerable 效率更高,但如果您想对数据进行一些操作并且如果您只想迭代数据,则返回列表也很有用,那么 IEnumerable 更可取。

      【讨论】:

      • 错了。 IEnumerable 只有一种方法。返回一个迭代器比构建一个列表要高效得多。
      • 另一种可能性是返回一个以只读方式实现ICollection(非泛型!)以及IEnumerable&lt;T&gt; 和可能ICollection&lt;T&gt; 的类型。如果实现了IEnumerable&lt;T&gt; 的类也实现了ICollection 或ICollection&lt;T&gt;,则IEnumerable&lt;T&gt; 的Count 扩展方法将使用这些接口之一的Count 方法。否则它将必须枚举所有项目才能获得计数。请注意,非泛型形式对此目的稍微有用一些,因为...
      • ...IEnumeration&lt;Cat&gt; 可以用作IEnumeration&lt;Animal&gt;,但ICollection&lt;Cat&gt; 不能用作ICollection&lt;Animal&gt;。如果给一个期望IEnumeration&lt;Animal&gt; 的例程提供了一个实现ICollection&lt;Cat&gt; 但不是非泛型ICollection 的类实例,它就无法知道集合应该被转换为ICollection&lt;Cat&gt; 以获得计数.但是,如果该类实现了非泛型 ICollection,则需要 IEnumerable&lt;Animal&gt; 的代码将很容易找到它。
      猜你喜欢
      • 2014-01-19
      • 1970-01-01
      • 1970-01-01
      • 2014-06-25
      • 2011-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-24
      相关资源
      最近更新 更多