表明IEnumerable是“慢”还是“快”的好方法是什么?

时间:2012-06-07 15:05:22

标签: .net performance ienumerable iqueryable

What is the expected performance of IEnumerable?的答案说,没有办法知道迭代任意IEnumerable的性能。每次迭代都可以访问数据库或进行Web服务调用;或者它可能只返回数组/列表中的下一个项目。

鉴于此,是否有一种很好的方式来表明“这很快”?例如,使用T[]List<T>代替IEnumerable<T>,我知道从T[i]T[i+1]的速度很快。 (当然,强制枚举返回列表/数组可能会产生其他性能问题。List<T>也会暴露可编辑的语义。)

相反,返回IQueryable<T>代替IEnumerable<T>是否表示“这很慢”?或者也许是IEnumerable<Task<T>>

C的客户无法知道IEnumerable<T>将具有截然不同的性能特征。

class C
{
   readonly T[] m_data;
   public IEnumerable<T> ThisWillIterateQuickly { get { return m_data; } }

   public IEnumeralbe<T> ThisWillIterateSlowly
   {
      get
      {
         T retval = ... an expensive database call ...;
         yield return retval;
      }
   }

   public IQueryable<T> IsThisBetterForSlow { get { return ThisWillIterateSlowly; } }
   public T[] IsThisAGoodWayForFast { get { return m_data; } }
 }

4 个答案:

答案 0 :(得分:3)

  

相反,返回IQueryable而不是IEnumerable是一个表明“这很慢”的好方法吗?

不,IQueryable<T>固有来自IEnumerable<T> ... IEnumerable<T>T的问题在于,IQueryable<T>在迭代时可能会产生巨大的副作用,例如查询远程数据库或类似的东西。

有趣的不是吗?

您可能应该询问IEnumerable<T> V.S. List<T>,其中第二个肯定有数据,并且不需要从其他地方获取数据。

答案 1 :(得分:2)

如果你想保证迭代不会包含任何意外,那么我同意,公开一个T[] - 它的枚举器不能被覆盖,因为你不能从数组继承。迭代也是无副作用的,IEnumerable<T>也不能说。

但是,我不同意公开数组 表达 此消息,这更为重要(在我看来)。除非您开始命名内容CheapIterationExpensiveIteration,否则某些内容的效果永远无法在代码中表达。

在硬币的另一面,使用数组,您只需将按需迭代的性能问题移动到填充数组的位置。 保证可以完全解决性能问题,因为它将完全迭代任何提供数组内容的内容。在IEnumerable<T>中,如果迭代停止,性能问题也会停止 - 最快的代码是不运行的代码。

答案 2 :(得分:1)

我通常使用属性来返回&#39; quick&#39; “慢&#39;”的枚举和方法。

您的问题是尽可能使用异步方法和设计的论据。然后,枚举所花费的时间并不重要,因为UI响应迅速且用户满意; - )

答案 3 :(得分:1)

在考虑了这个之后,问题/问题似乎真正集中在IEnumerator.MoveNext()的行为/表现上。使用 Visual Studio 2012 ,我能够创建IEnumeratorIEnumerable的异步版本:

public interface IAsyncEnumerator<T> : IDisposable
{
    Task<T> CurrentAsync { get; }
    Task<bool> MoveNextAsync();
    Task ResetAsync();
}

public interface IAsyncEnumerable<T>
{
    IAsyncEnumerator<T> GetAsyncEnumerator();
}

这种方法的缺点是没有很多语言支持;以上内容不适用于foreach。但是,扩展方法可以缓解疼痛:

public static class EnumeratorExtensions
{
    public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        using (var enumerator = enumerable.GetEnumerator())
        {
            while (enumerator.MoveNext())
                action(enumerator.Current);
        }
    }

    public static async Task ForEachAsync<T>(this IAsyncEnumerable<T> enumerable, Action<T> action)
    {
        using (var enumerator = enumerable.GetAsyncEnumerator())
        {
            while (await enumerator.MoveNextAsync())
                action(await enumerator.CurrentAsync);
        }
    }
}