C# 在不迭代的情况下计算 IEnumerable<T> 中的项目?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/168901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Count the items from a IEnumerable<T> without iterating?
提问by sebagomez
private IEnumerable<string> Tables
{
get
{
yield return "Foo";
yield return "Bar";
}
}
Let's say I want iterate on those and write something like processing #n of #m.
假设我想对这些进行迭代并编写诸如处理 #n of #m 之类的内容。
Is there a way I can find out the value of m without iterating before my main iteration?
有没有一种方法可以在我的主迭代之前不迭代地找出 m 的值?
I hope I made myself clear.
我希望我说清楚了。
采纳答案by Mendelt
IEnumerable
doesn't support this. This is by design. IEnumerable
uses lazy evaluation to get the elements you ask for just before you need them.
IEnumerable
不支持这个。这是设计使然。IEnumerable
使用惰性求值在您需要它们之前获取您要求的元素。
If you want to know the number of items without iterating over them you can use ICollection<T>
, it has a Count
property.
如果你想知道项目的数量而不迭代它们,你可以使用ICollection<T>
,它有一个Count
属性。
回答by JesperE
No, not in general. One point in using enumerables is that the actual set of objects in the enumeration is not known (in advance, or even at all).
不,不是一般的。使用枚举的一点是枚举中的实际对象集是未知的(事先,甚至根本不知道)。
回答by Joel Coehoorn
A friend of mine has a series of blog posts that provide an illustration for why you can't do this. He creates function that return an IEnumerable where each iteration returns the next prime number, all the way to ulong.MaxValue
, and the next item isn't calculated until you ask for it. Quick, pop question: how many items are returned?
我的一个朋友发表了一系列博客文章,说明了为什么不能这样做。他创建了返回 IEnumerable 的函数,其中每次迭代都返回下一个素数,一直到ulong.MaxValue
,并且在您要求之前不会计算下一项。快速、流行的问题:退回了多少件商品?
Here are the posts, but they're kind of long:
这是帖子,但它们有点长:
- Beyond Loops(provides an initial EnumerableUtility class used in the other posts)
- Applications of Iterate(Initial implementation)
- Crazy Extention Methods: ToLazyList(Performance optimizations)
- Beyond Loops(提供其他帖子中使用的初始 EnumerableUtility 类)
- Iterate的应用(初步实现)
- 疯狂的扩展方法:ToLazyList(性能优化)
回答by Chris Ammerman
IEnumerable cannot count without iterating.
IEnumerable 不迭代就无法计数。
Under "normal" circumstances, it would be possible for classes implementing IEnumerable or IEnumerable<T>, such as List<T>, to implement the Count method by returning the List<T>.Count property. However, the Count method is not actually a method defined on the IEnumerable<T> or IEnumerable interface. (The only one that is, in fact, is GetEnumerator.) And this means that a class-specific implementation cannot be provided for it.
在“正常”情况下,实现 IEnumerable 或 IEnumerable<T> 的类(例如 List<T>)可以通过返回 List<T>.Count 属性来实现 Count 方法。但是,Count 方法实际上并不是在 IEnumerable<T> 或 IEnumerable 接口上定义的方法。(实际上,唯一一个是 GetEnumerator。)这意味着不能为其提供特定于类的实现。
Rather, Count it is an extension method, defined on the static class Enumerable. This means it can be called on any instance of an IEnumerable<T> derived class, regardless of that class's implementation. But it also means it is implemented in a single place, external to any of those classes. Which of course means that it must be implemented in a way that is completely independent of these class' internals. The only such way to do counting is via iteration.
相反,Count 它是一个扩展方法,定义在静态类 Enumerable 上。这意味着它可以在 IEnumerable<T> 派生类的任何实例上调用,而不管该类的实现如何。但这也意味着它是在一个地方实现的,在任何这些类的外部。这当然意味着它必须以完全独立于这些类内部的方式实现。进行计数的唯一方法是通过迭代。
回答by Robert Paulson
Just adding extra some info:
只需添加额外的一些信息:
The Count()
extension doesn't always iterate. Consider Linq to Sql, where the count goes to the database, but instead of bringing back all the rows, it issues the Sql Count()
command and returns that result instead.
该Count()
扩展并不总是迭代。考虑 Linq 到 Sql,其中计数进入数据库,但不是带回所有行,而是发出 SqlCount()
命令并返回该结果。
Additionally, the compiler (or runtime) is smart enough that it will call the objects Count()
method if it has one. So it's notas other responders say, being completely ignorant and always iterating in order to count elements.
此外,编译器(或运行时)足够聪明,它会调用对象Count()
方法(如果有的话)。所以它不像其他响应者所说的那样,完全无知并且总是迭代以计算元素。
In many cases where the programmer is just checking if( enumerable.Count != 0 )
using the Any()
extension method, as in if( enumerable.Any() )
is far more efficient with linq's lazy evaluation as it can short-circuit once it can determine there are any elements. It's also more readable
在许多情况下,程序员只是if( enumerable.Count != 0 )
使用Any()
扩展方法进行检查,因为if( enumerable.Any() )
使用 linq 的惰性求值效率更高,因为一旦确定存在任何元素,它就可以短路。它也更具可读性
回答by Jonathan Allen
I would suggest calling ToList. Yes you are doing the enumeration early, but you still have access to your list of items.
我建议调用 ToList。是的,您正在尽早进行枚举,但您仍然可以访问您的项目列表。
回答by Jonathan Allen
Alternatively you can do the following:
或者,您可以执行以下操作:
Tables.ToList<string>().Count;
回答by JP Alioto
Here is a great discussion about lazy evaluationand deferred execution. Basically you have to materialize the list to get that value.
回答by Daniel Earwicker
The System.Linq.Enumerable.Count
extension method on IEnumerable<T>
has the following implementation:
该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;
So it tries to cast to ICollection<T>
, which has a Count
property, and uses that if possible. Otherwise it iterates.
因此,它尝试强制转换为ICollection<T>
具有Count
属性的to ,并在可能的情况下使用它。否则它会迭代。
So your best bet is to use the Count()
extension method on your IEnumerable<T>
object, as you will get the best performance possible that way.
因此,最好的Count()
办法是在IEnumerable<T>
对象上使用扩展方法,因为这样可以获得最佳性能。
回答by Samuel Hyman
Going beyond your immediate question (which has been thoroughly answered in the negative), if you're looking to report progress whilst processing an enumerable, you might want to look at my blog post Reporting Progress During Linq Queries.
超越您的直接问题(已完全否定回答),如果您希望在处理可枚举项时报告进度,您可能需要查看我的博客文章报告 Linq 查询期间的进度。
It lets you do this:
它可以让你这样做:
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
};