从IEnumerable <T>计算项目而不迭代?

seb*_*mez 304 .net c# ienumerable

private IEnumerable<string> Tables
{
    get
    {
        yield return "Foo";
        yield return "Bar";
    }
}
Run Code Online (Sandbox Code Playgroud)

假设我想迭代那些并写一些像#m的#n处理.

有没有一种方法可以在我的主迭代之前找到m的值而不进行迭代?

我希望我清楚自己.

Men*_*elt 322

IEnumerable不支持这个.这是设计的.IEnumerable在您需要之前使用延迟评估来获取您要求的元素.

如果你想知道项目的数量,而不是迭代它们你可以使用ICollection<T>,它有一个Count属性.

  • 如果您不需要通过索引器访问列表,我更喜欢ICollection而不是IList. (37认同)
  • @Shimmy你迭代并计算元素.或者从Linq命名空间调用Count()来为您执行此操作. (21认同)
  • 我通常只是习惯于抓住List和IList.但特别是如果你想自己实现ICollection更容易,并且还有Count属性.谢谢! (3认同)

Dan*_*ker 208

System.Linq.Enumerable.Count对扩展方法IEnumerable<T>有以下实现:

ICollection<T> c = source as ICollection<TSource>;
if (c != null)
    return c.Count;

int result = 0;
using (IEnumerator<T> enumerator = source.GetEnumerator())
{
    while (enumerator.MoveNext())
        result++;
}
return result;
Run Code Online (Sandbox Code Playgroud)

所以它试图转换为ICollection<T>具有Count属性的属性,并在可能的情况下使用它.否则它会迭代.

因此,最好的Count()办法是在IEnumerable<T>对象上使用扩展方法,因为这样可以获得最佳性能.

  • 非常有趣的是它首先尝试强制转换为"ICollection <T>". (12认同)
  • @Jaider - 比这稍微复杂一点.`IEnumerable <T>`继承`IDisposable`,它允许`using`语句自动处理它.`IEnumerable`没有.因此,如果你以任何一种方式调用`GetEnumerator`,你应该以`var d = e作为IDisposable完成; if(d!= null)d.Dispose();` (6认同)
  • 遗憾的是,.NET 在 IEnumerable 和 ICollection 之间没有中间接口,用于具有预先已知计数的 IEnumerables(但不需要 ICollection 的其他功能)。也没有任何方法可以知道,给定一个任意的 IEnumerable,并且需要在算法的早期知道“Count”,调用 IEnumerable.Count 是否会更便宜,从而第二次迭代收集,或者收集到列表中一次,以便您进行计数,然后使用该列表。更糟糕的是,迭代两次可能会有“副作用”。另一方面,枚举可能有很多元素 (2认同)

Rob*_*son 86

只需添加额外的一些信息:

Count()扩展并不总是迭代.考虑Linq to Sql,其中计数进入数据库,但不是返回所有行,而是发出Sql Count()命令并返回该结果.

此外,编译器(或运行时)足够智能,Count()如果有一个对象方法,它将调用它.所以它并不像其他响应者所说的那样,完全无知并且总是为了计算元素而迭代.

在许多情况下,程序员只是if( enumerable.Count != 0 )使用Any()扩展方法进行检查,因为if( enumerable.Any() ) linq的惰性评估效率更高,因为一旦确定有任何元素,它就会短路.它也更具可读性

  • +1 for.任何用于测试没有元素的 (20认同)
  • 关于集合和数组。如果您碰巧使用了集合,请使用`.Count`属性,因为它始终知道其大小。查询`collection.Count`时,没有额外的计算,它只返回已知的计数。据我所知,与Array.length相同。但是,.Any()使用(IEnumerator &lt;TSource&gt; enumerator = source.GetEnumerator())获取源的枚举,如果可以执行enumerator.MoveNext(),则返回true。对于集合:“ if(collection.Count&gt; 0)”,数组:“ if(array.length&gt; 0)”,对于可枚举,则执行“ if(collection.Any())”。 (2认同)

Joe*_*orn 12

我的一位朋友有一系列博客文章,说明了为什么你不能这样做.他创建了返回IEnumerable的函数,其中每次迭代返回下一个素数,一直到ulong.MaxValue,并且在您要求之前不会计算下一个项目.快速流行问题:返回了多少项?

这是帖子,但它们有点长:

  1. Beyond循环(提供其他帖子中使用的初始EnumerableUtility类)
  2. 迭代的应用(初始实现)
  3. 疯狂扩展方法:ToLazyList(性能优化)

  • 我真的希望MS已经定义了一种方法来让enumerables描述他们对自己的看法("不知道任何事情"是一个有效的答案).没有任何困难可以回答诸如"你知道自己是有限的"这样的问题,"你知道自己是少于N个元素的有限",以及"你知道自己是无限的",因为任何可以枚举的人都可以合理地(如果没有帮助)对所有人回答"否".如果有一个标准的方法可以提出这样的问题,那么对于普查员来说,返回无穷无尽的序列会更安全...... (2认同)

Chr*_*man 10

没有迭代,IEnumerable不能计数.

在"正常"情况下,实现IEnumerable或IEnumerable <T>的类(例如List <T>)可以通过返回List <T> .Count属性来实现Count方法.但是,Count方法实际上不是在IEnumerable <T>或IEnumerable接口上定义的方法.(事实上​​,唯一的一个是GetEnumerator.)这意味着无法为它提供特定于类的实现.

相反,Count它是一个扩展方法,在静态类Enumerable上定义.这意味着它可以在IEnumerable <T>派生类的任何实例上调用,而不管该类的实现如何.但它也意味着它在一个地方实施,在任何这些类之外.这当然意味着它必须以完全独立于这些类的内部的方式实现.唯一这样的计数方法是通过迭代.


小智 9

或者,您可以执行以下操作:

Tables.ToList<string>().Count;
Run Code Online (Sandbox Code Playgroud)


Jes*_*erE 8

不,不是一般的.使用枚举的一点是,枚举中的实际对象集是未知的(事先,甚至根本不知道).


pro*_*eek 8

您可以使用System.Linq.

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    private IEnumerable<string> Tables
    {
        get {
             yield return "Foo";
             yield return "Bar";
         }
    }

    static void Main()
    {
        var x = new Test();
        Console.WriteLine(x.Tables.Count());
    }
}
Run Code Online (Sandbox Code Playgroud)

你会得到结果'2'.

  • 这不适用于非泛型变体IEnumerable(没有类型说明符) (3认同)

Sow*_*Roy 8

我认为最简单的方法是做到这一点

Enumerable.Count<TSource>(IEnumerable<TSource> source)
Run Code Online (Sandbox Code Playgroud)

参考:system.linq.enumerable


Sam*_*ack 5

超越您的直接问题(已经完全回答了否定),如果您希望在处理可枚举时报告进度,您可能需要查看我的博客帖子报告Linq查询期间的进度.

它可以让你这样做:

BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += (sender, e) =>
      {
          // pretend we have a collection of 
          // items to process
          var items = 1.To(1000);
          items
              .WithProgressReporting(progress => worker.ReportProgress(progress))
              .ForEach(item => Thread.Sleep(10)); // simulate some real work
      };
Run Code Online (Sandbox Code Playgroud)


小智 5

我在方法中使用这种方式来检查传入的IEnumberable内容

if( iEnum.Cast<Object>().Count() > 0) 
{

}
Run Code Online (Sandbox Code Playgroud)

在这样的方法中:

GetDataTable(IEnumberable iEnum)
{  
    if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further

}
Run Code Online (Sandbox Code Playgroud)


Rus*_*ell 5

LINQ for .NET 6 中有一种新方法观看https://www.youtube.com/watch?v=sIXKpyhxHR8

Tables.TryGetNonEnumeratedCount(out var count)
Run Code Online (Sandbox Code Playgroud)


小智 5

简化所有答案。

IEnumerable 没有 Count 函数或属性。为此,您可以存储计数变量(例如,使用 foreach)或使用Linq求解来获取计数。

如果你有:

IEnumerable<> 产品

然后:

声明:“使用 System.Linq;”

计数:

产品.ToList().Count