347
private IEnumerable<string> Tables
{
    get
    {
        yield return "Foo";
        yield return "Bar";
    }
}

假设我想对这些进行迭代并编写诸如处理#n of #m 之类的东西。

有没有办法在我的主迭代之前不迭代就可以找出 m 的值?

我希望我说清楚了。

4

21 回答 21

373

IEnumerable不支持这个。这是设计使然。IEnumerable使用惰性求值在您需要之前获取您要求的元素。

如果您想知道项目的数量而不对其进行迭代,您可以使用ICollection<T>,它有一个Count属性。

于 2008-10-03T21:08:33.977 回答
248

System.Linq.Enumerable.Count扩展方法 on具有以下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;

因此,它尝试强制转换为ICollection<T>具有Count属性的 ,并在可能的情况下使用该属性。否则它会迭代。

所以你最好的办法是Count()在你的对象上使用扩展方法IEnumerable<T>,因为这样你会得到最好的性能。

于 2009-05-12T15:48:20.857 回答
98

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

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

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

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

于 2008-10-04T03:49:04.277 回答
13

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

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

  1. Beyond Loops(提供其他帖子中使用的初始 EnumerableUtility 类)
  2. Iterate的应用(初步实现)
  3. 疯狂的扩展方法:ToLazyList(性能优化)
于 2008-10-03T21:18:39.120 回答
12

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

Tables.ToList<string>().Count;
于 2009-05-12T15:35:35.417 回答
11

IEnumerable 不迭代就无法计数。

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

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

于 2008-10-03T21:40:29.683 回答
9

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

于 2008-10-03T21:07:40.233 回答
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());
    }
}

你会得到结果'2'。

于 2012-01-26T19:02:13.763 回答
7

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

Enumerable.Count<TSource>(IEnumerable<TSource> source)

参考:system.linq.enumerable

于 2020-04-18T02:56:51.223 回答
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
      };
于 2009-05-12T15:56:18.550 回答
5

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

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

}

在这样的方法中:

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

}
于 2012-11-02T07:58:29.873 回答
4

这取决于 .Net 的版本和 IEnumerable 对象的实现。Microsoft 已修复 IEnumerable.Count 方法来检查实现,并使用 ICollection.Count 或 ICollection<TSource>.Count,请参阅此处的详细信息https://connect.microsoft.com/VisualStudio/feedback/details/454130

下面是来自 Ildasm 的 System.Core 的 MSIL,System.Linq 位于其中。

.method public hidebysig static int32  Count<TSource>(class 

[mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource> source) cil managed
{
  .custom instance void System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  // Code size       85 (0x55)
  .maxstack  2
  .locals init (class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource> V_0,
           class [mscorlib]System.Collections.ICollection V_1,
           int32 V_2,
           class [mscorlib]System.Collections.Generic.IEnumerator`1<!!TSource> V_3)
  IL_0000:  ldarg.0
  IL_0001:  brtrue.s   IL_000e
  IL_0003:  ldstr      "source"
  IL_0008:  call       class [mscorlib]System.Exception System.Linq.Error::ArgumentNull(string)
  IL_000d:  throw
  IL_000e:  ldarg.0
  IL_000f:  isinst     class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>
  IL_0014:  stloc.0
  IL_0015:  ldloc.0
  IL_0016:  brfalse.s  IL_001f
  IL_0018:  ldloc.0
  IL_0019:  callvirt   instance int32 class [mscorlib]System.Collections.Generic.ICollection`1<!!TSource>::get_Count()
  IL_001e:  ret
  IL_001f:  ldarg.0
  IL_0020:  isinst     [mscorlib]System.Collections.ICollection
  IL_0025:  stloc.1
  IL_0026:  ldloc.1
  IL_0027:  brfalse.s  IL_0030
  IL_0029:  ldloc.1
  IL_002a:  callvirt   instance int32 [mscorlib]System.Collections.ICollection::get_Count()
  IL_002f:  ret
  IL_0030:  ldc.i4.0
  IL_0031:  stloc.2
  IL_0032:  ldarg.0
  IL_0033:  callvirt   instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> class [mscorlib]System.Collections.Generic.IEnumerable`1<!!TSource>::GetEnumerator()
  IL_0038:  stloc.3
  .try
  {
    IL_0039:  br.s       IL_003f
    IL_003b:  ldloc.2
    IL_003c:  ldc.i4.1
    IL_003d:  add.ovf
    IL_003e:  stloc.2
    IL_003f:  ldloc.3
    IL_0040:  callvirt   instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    IL_0045:  brtrue.s   IL_003b
    IL_0047:  leave.s    IL_0053
  }  // end .try
  finally
  {
    IL_0049:  ldloc.3
    IL_004a:  brfalse.s  IL_0052
    IL_004c:  ldloc.3
    IL_004d:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0052:  endfinally
  }  // end handler
  IL_0053:  ldloc.2
  IL_0054:  ret
} // end of method Enumerable::Count
于 2011-04-25T05:18:23.793 回答
3

IEnumerable.Count() 函数的结果可能是错误的。这是一个非常简单的测试示例:

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

namespace Test
{
  class Program
  {
    static void Main(string[] args)
    {
      var test = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
      var result = test.Split(7);
      int cnt = 0;

      foreach (IEnumerable<int> chunk in result)
      {
        cnt = chunk.Count();
        Console.WriteLine(cnt);
      }
      cnt = result.Count();
      Console.WriteLine(cnt);
      Console.ReadLine();
    }
  }

  static class LinqExt
  {
    public static IEnumerable<IEnumerable<T>> Split<T>(this IEnumerable<T> source, int chunkLength)
    {
      if (chunkLength <= 0)
        throw new ArgumentOutOfRangeException("chunkLength", "chunkLength must be greater than 0");

      IEnumerable<T> result = null;
      using (IEnumerator<T> enumerator = source.GetEnumerator())
      {
        while (enumerator.MoveNext())
        {
          result = GetChunk(enumerator, chunkLength);
          yield return result;
        }
      }
    }

    static IEnumerable<T> GetChunk<T>(IEnumerator<T> source, int chunkLength)
    {
      int x = chunkLength;
      do
        yield return source.Current;
      while (--x > 0 && source.MoveNext());
    }
  }
}

结果必须是 (7,7,3,3) 但实际结果是 (7,7,3,17)

于 2009-08-12T16:41:30.623 回答
3

LINQ 中有一种新方法用于 .NET 6 Watch https://www.youtube.com/watch?v=sIXKpyhxHR8

Tables.TryGetNonEnumeratedCount(out var count)
于 2021-11-03T15:59:27.920 回答
2

这是关于惰性评估延迟执行的精彩讨论。基本上,您必须具体化列表才能获得该值。

于 2009-05-12T15:41:50.040 回答
2

我发现的最好方法是通过将其转换为列表来计数。

IEnumerable<T> enumList = ReturnFromSomeFunction();

int count = new List<T>(enumList).Count;
于 2020-01-06T12:14:51.837 回答
0

简化所有答案。

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

如果你有:

IEnumerable<> 产品

然后:

声明:“使用 System.Linq;”

计数:

products.ToList().Count

于 2022-01-18T10:42:28.450 回答
-1

我建议打电话给 ToList。是的,您正在尽早进行枚举,但您仍然可以访问您的项目列表。

于 2008-10-04T05:00:20.780 回答
-1

它可能不会产生最佳性能,但您可以使用 LINQ 计算 IEnumerable 中的元素:

public int GetEnumerableCount(IEnumerable Enumerable)
{
    return (from object Item in Enumerable
            select Item).Count();
}
于 2013-06-26T08:39:09.027 回答
-2

我使用IEnum<string>.ToArray<string>().Length它,它工作正常。

于 2012-01-18T09:51:48.170 回答
-6

如果我有字符串列表,我会使用这样的代码:

((IList<string>)Table).Count
于 2018-05-07T08:28:36.097 回答