在.NET中,用于确定IEnumerable是否为空的坚实,优雅,可重用的代码是什么?
我试图找到最可重用但最优雅的代码段,以确定是否IEnumerable。理想情况下,这应该是一个函数,我可以在需要告诉IEnumerable是否为空的任何时候绝对调用它。
虽然我已经为.NET 3.5开发了一个到目前为止一直很好的答案,但是我目前的想法是没有完美的答案,因为IEnumerable可以从技术上封装将基础结果修改为一个集合(或者迭代器队列),因为它会迭代,这会引起问题。但是,这也将成为实现IEnumerable.Count()的障碍,并且这不会阻止MS提供它。
因此,我想我应该把它放在SO上,看看是否有人有更好的东西,以防其他人觉得它有用。
编辑:哇,我不敢相信我不知道IEnumerable.Any。我知道它的存在,但是从不费心去检查它的作用。让这成为一个教训。阅读文档。仅仅因为方法名称并不意味着它就可以满足要求,并不意味着它就不能满足要求。
解决方案
!enumerable.Any()
将尝试仅获取第一个元素。
为了扩展其工作方式/原因,任何函数都会确定IEnumerable的任何组件是否与给定函数匹配,如果没有给出任何组件,则任何组件都会成功,这意味着如果可枚举中存在元素,则该函数将返回true。 。
对于.net 1/2:
IEnumerator e; try { e = enumerable.GetEnumerator(); return e.MoveNext(); } finally { if (e is IDisposable) e.Dispose(); }
或者,使用泛型:
using (IEnumerator<T> e = enumerable.GetEnumerator()) { return e.MoveNext(); }
使用这两种方法要注意的一件事是,并非所有枚举都可以回滚,例如System.Data.IDataReader的所有实现只能运行一次。
在这些情况下,只要考虑到它甚至可能不会循环一次的事实,我们实际上就不会在foreach
循环中产生任何成本。
我们说对了,没有完美的答案。 IEnumerable仅支持迭代,并且不保证枚举是可重复的。我们无法找出枚举是否包含元素而至少不调用MoveNext一次,一旦这样做,就无法保证能够重用该枚举:IEnumerable.Reset允许抛出NotSupportedException。从http://msdn.microsoft.com/zh-cn/library/system.collections.ienumerator.reset.aspx:
"提供Reset方法是为了实现COM的互操作性。不一定需要实现它;相反,实现者可以简单地抛出NotSupportedException。"
扩展方法,例如IEnumerable <T> .Count和IEnumerable <T>。任何需要在幕后调用MoveNext的方法。有用的包装器,但要避免这样的事实:在(很少)枚举不支持Reset的情况下,我们可能会遇到问题。
第一种方法具有Empty扩展方法的简单实现:
http://signum.codeplex.com/SourceControl/changeset/view/25903#510468