7

IEnumerable 的预期性能是什么?说没有办法知道迭代任意 IEnumerable 的性能。每次迭代都可以访问数据库或进行网络服务调用;或者它可能只返回数组/列表中的下一项。

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

相反,返回IQueryable<T>而不是IEnumerable<T>表示“这很慢”的好方法吗?或者也许IEnumerable<Task<T>>

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

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

4 回答 4

3

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

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

好笑不是吗?

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

于 2012-06-07T15:07:47.087 回答
2

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

但是,我不同意公开数组表达此消息,这更重要(在我看来)。某些东西的性能永远无法真正用代码来表达,除非你开始命名东西CheapIterationExpensiveIteration.

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

于 2012-06-07T15:11:22.363 回答
1

在考虑了更多之后,问题/问题似乎真正集中在IEnumerator.MoveNext(). 使用Visual Studio 2012,我能够创建IEnumerator和的异步版本IEnumerable

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);
        }
    }
}
于 2012-06-08T14:47:37.590 回答
1

我通常使用属性来返回“快速”枚举和方法来返回“慢”。

您的问题是尽可能多地使用异步方法和设计的论据。然后,枚举需要多长时间并不重要,因为 UI 是响应式的并且用户很高兴;-)

于 2012-06-07T15:59:33.573 回答