private IEnumerable<string> Tables
{
get
{
yield return "Foo";
yield return "Bar";
}
}
假设我想要迭代这些并编写类似#m处理#n的东西。
有没有办法可以在主迭代之前找不到m的值而不进行迭代?
我希望我能说清楚。
答案 0 :(得分:308)
IEnumerable
不支持此功能。这是设计的。 IEnumerable
使用延迟评估来在您需要之前获取您要求的元素。
如果你想知道没有迭代它们的项目数量,你可以使用ICollection<T>
,它有一个Count
属性。
答案 1 :(得分:197)
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;
因此它尝试强制转换为具有ICollection<T>
属性的Count
,并在可能的情况下使用它。否则它会迭代。
因此,最好的办法是在Count()
对象上使用IEnumerable<T>
扩展方法,因为这样可以获得最佳性能。
答案 2 :(得分:84)
只需添加额外的一些信息:
Count()
扩展程序并不总是迭代。考虑Linq to Sql,其中计数进入数据库,但不是返回所有行,而是发出Sql Count()
命令并返回该结果。
此外,编译器(或运行时)足够智能,如果它有一个,它将调用对象Count()
方法。所以它是不,正如其他响应者所说,完全无知并且总是迭代以便计算元素。
在许多情况下,程序员只是使用if( enumerable.Count != 0 )
扩展方法检查Any()
,因为在if( enumerable.Any() )
中使用linq的惰性评估会更有效,因为它可能会短路一次可以确定有任何元素。它也更具可读性
答案 3 :(得分:11)
我的一位朋友有一系列博客文章,说明了为什么你不能这样做。他创建了返回IEnumerable的函数,其中每次迭代返回下一个素数,一直到ulong.MaxValue
,并且在您要求之前不会计算下一个项目。快速流行问题:返回了多少项?
以下是帖子,但它们有点长:
答案 4 :(得分:10)
IEnumerable在没有迭代的情况下无法计算。
在“正常”情况下,实现IEnumerable或IEnumerable&lt; T&gt;的类(例如List&lt; T&gt;)可以通过返回List&lt; T&gt; .Count属性来实现Count方法。然而,Count方法实际上不是在IEnumerable&lt; T&gt;上定义的方法。或IEnumerable接口。 (事实上,唯一的一个是GetEnumerator。)这意味着无法为它提供特定于类的实现。
相反,Count它是一个扩展方法,在静态类Enumerable上定义。这意味着它可以在IEnumerable&lt; T&gt;的任何实例上调用。派生类,无论该类的实现如何。但它也意味着它在一个地方实施,在任何这些类之外。这当然意味着它必须以完全独立于这些类的内部的方式实现。唯一这样的计数方法是通过迭代。
答案 5 :(得分:9)
或者,您可以执行以下操作:
Tables.ToList<string>().Count;
答案 6 :(得分: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'。
答案 7 :(得分:8)
不,不是一般的。使用枚举的一点是,枚举中的实际对象集是未知的(事先,甚至根本不知道)。
答案 8 :(得分:5)
超出您的直接问题(已经完全回答了否定),如果您希望在处理可枚举时报告进度,您可能需要查看我的博文Reporting Progress During Linq Queries。
它可以让你这样做:
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
};
答案 9 :(得分:4)
我在方法中使用这种方式检查传入的IEnumberable
内容
if( iEnum.Cast<Object>().Count() > 0)
{
}
在这样的方法中:
GetDataTable(IEnumberable iEnum)
{
if (iEnum != null && iEnum.Cast<Object>().Count() > 0) //--- proceed further
}
答案 10 :(得分:3)
这取决于.Net的哪个版本和IEnumerable对象的实现。 Microsoft已修复IEnumerable.Count方法以检查实现,并使用ICollection.Count或ICollection&lt; TSource&gt; .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
答案 11 :(得分:2)
以下是关于lazy evaluation和deferred execution的精彩讨论。基本上你必须具体化列表才能获得该值。
答案 12 :(得分:2)
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)
答案 13 :(得分:1)
快速计数的唯一方法是原始集合具有索引器(如数组)。为了创建具有最低要求的通用代码,您可以使用IEnumerable,但如果您还需要计数,那么我首选的方法是使用此接口:
public interface IEnumAndCount<out T> : IEnumerable<T>
{
int Count { get; }
}
如果您的原始集合没有任何索引器,则您的Count实现可以迭代集合,其中已知性能为O(n)。
如果你不想使用类似于IEnumAndCount的东西,你最好的选择是使用Linq.Count,理由是Daniel Earwicker在这个问题的顶部附近给出的理由。
祝你好运!答案 14 :(得分:0)
我建议调用ToList。是的,您提前进行了枚举,但仍然可以访问您的项目列表。
答案 15 :(得分:0)
没有
您是否在您编写的代码中的任何位置看到了这些信息?
您可能认为编译器可以“看到”只有两个,但这意味着它需要分析每个迭代器方法,只查找该特定的病态情况。即使它确实如此,考虑到IEnumerable的限制,你会怎么看?
答案 16 :(得分:0)
它可能无法产生最佳性能,但您可以使用LINQ来计算IEnumerable中的元素:
public int GetEnumerableCount(IEnumerable Enumerable)
{
return (from object Item in Enumerable
select Item).Count();
}
答案 17 :(得分:-1)
答案 18 :(得分:-2)
我使用IEnum<string>.ToArray<string>().Length
并且效果很好。
答案 19 :(得分:-2)
如果我有字符串列表,我会使用这样的代码:
((IList<string>)Table).Count