如何:从IEnumerable <T>计数项目而不迭代?

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

假设我想对这些进行迭代,并写入#n处理#m的内容。

有没有一种方法,我可以findm的价值没有迭代之前,我的主要迭代?

我希望我自己清楚。

IEnumerable不支持这个。 这是devise。 IEnumerable使用懒惰评估来获取你需要的元素。

如果你想知道没有迭代的项目数,你可以使用ICollection<T> ,它有一个Count属性。

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

 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属性,如果可能的话使用它。 否则它迭代。

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

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

Count()扩展并不总是迭代。 考虑Linq到Sql,数到达数据库的位置,但不是将所有行都带回来,而是发出Sql Count()命令并返回该结果。

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

在很多情况下,程序员只是使用Any()扩展方法来检查if( enumerable.Count != 0 ) ,如if( enumerable.Any() )在linq的懒惰评估中效率更高,因为它可以短路一旦它可以确定有任何元素。 它也更可读

IEnumerable不能迭代计数。

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

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

我的一个朋友有一系列的博客文章,提供了为什么你不能这样做的插图。 他创build的函数返回一个IEnumerable,其中每个迭代都返回下一个素数,一直到ulong.MaxValue ,并且下一个项目不会被计算,直到您要求为止。 快速,stream行的问题:有多less物品被退回?

这里是post,但是它们很长:

  1. Beyond Loops (提供了其他职位中使用的初始EnumerableUtility类)
  2. 迭代的应用 (初始实现)
  3. Crazy Extention方法:ToLazyList (性能优化)

不,一般来说。 使用枚举数的一个要点是枚举中的实际对象集是不知道的(事先,甚至完全不知道)。

你可以使用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'。

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

 Tables.ToList<string>().Count; 

超出您的直接问题(已被彻底回答否定),如果您希望在处理枚举时报告进度,您可能需要查看我的博客文章“在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 }; 

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

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

在这样的方法里面:

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

这里是关于懒惰评估和延期执行的一个很好的讨论。 基本上你必须实现清单来获得这个价值。

IEnumerable.Count()函数的结果可能是错误的。 这是一个非常简单的样本来testing:

 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)

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

下面是System.Linq驻留的System.Core的Ildasm的MSIL。

 .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 

至less在没有内部迭代的情况下你不能做到这一点。 这将工作。

 using System.Linq; ... Tables.Count(); 

有一个快速计数的唯一方法是当原始集合有一个索引器(如数组)。 为了创build具有最低要求的generics代码,您可以使用IEnumerable,但是如果您需要计数,那么我的首选方法是使用此接口:

 public interface IEnumAndCount<out T> : IEnumerable<T> { int Count { get; } } 

如果您的原始集合中没有任何索引器,则您的Count实现可以迭代集合,并具有已知的性能O(n)。

如果你不想使用类似于IEnumAndCount的东西,那么最好的办法就是用Linq.Count来理解Daniel Earwicker给出的问题。

祝你好运 !

没有。

你有没有在你写的代码中的任何地方看到这些信息?

你可能会争辩说,编译器可以“看到”只有两个,但这意味着它需要分析每个迭代器的方法,只是为了特定的病态。 即使它确实如此,鉴于IEnumerable的限制,您将如何阅读它?

我会build议调用ToList。 是的,你正在尽早的枚举,但你仍然可以访问你的项目列表。

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

 public int GetEnumerableCount(IEnumerable Enumerable) { return (from object Item in Enumerable select Item).Count(); } 

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