【发布时间】:2011-08-02 00:41:05
【问题描述】:
谁能帮我解决IEnumerable 的Count 扩展方法(非通用接口)。
我知道 LINQ 不支持,但是如何手动编写呢?
【问题讨论】:
标签: c# .net linq extension-methods ienumerable
谁能帮我解决IEnumerable 的Count 扩展方法(非通用接口)。
我知道 LINQ 不支持,但是如何手动编写呢?
【问题讨论】:
标签: c# .net linq extension-methods ienumerable
我认为首先选择代表元素序列的类型应该是 ICollection 而不是 IEnumerable。
ICollection 和 ICollection<T> 都提供了一个 Count 属性,而且每个 ICollection 都实现了 IEnumearable。
【讨论】:
最简单的形式是:
public static int Count(this IEnumerable source)
{
int c = 0;
using (var e = source.GetEnumerator())
{
while (e.MoveNext())
c++;
}
return c;
}
然后您可以通过查询ICollection 来改进这一点:
public static int Count(this IEnumerable source)
{
var col = source as ICollection;
if (col != null)
return col.Count;
int c = 0;
using (var e = source.GetEnumerator())
{
while (e.MoveNext())
c++;
}
return c;
}
更新
正如 Gerard 在 cmets 中指出的那样,非泛型 IEnumerable 不会继承 IDisposable,因此正常的 using 语句将不起作用。如果可能,尝试处理此类枚举器可能仍然很重要——迭代器方法实现了IEnumerable,因此可以间接传递给Count 方法。在内部,该迭代器方法将依赖于对Dispose 的调用来触发它自己的try/finally 和using 语句。
为了在其他情况下也能轻松做到这一点,您可以制作自己的 using 语句版本,在编译时不那么繁琐:
public static void DynamicUsing(object resource, Action action)
{
try
{
action();
}
finally
{
IDisposable d = resource as IDisposable;
if (d != null)
d.Dispose();
}
}
更新后的Count 方法将是:
public static int Count(this IEnumerable source)
{
var col = source as ICollection;
if (col != null)
return col.Count;
int c = 0;
var e = source.GetEnumerator();
DynamicUsing(e, () =>
{
while (e.MoveNext())
c++;
});
return c;
}
【讨论】:
using 给出了System.Collections.IEnumerable 的编译错误。我将代码更改为 Count<T>(this IEnumerable<T> source) 编译。
不同类型的IEnumerable有不同的确定count的最优方法;不幸的是,没有通用的方法可以知道哪种方法最适合任何给定的 IEnumerable,甚至没有任何标准方法可以让 IEmumerable 指示以下哪种技术最好:
在不同的情况下,上面的每一个都是最优的。
【讨论】:
yourEnumerable.Cast<object>().Count()
关于性能的评论:
我认为这是过早优化的一个很好的例子,但是你去吧:
static class EnumerableExtensions
{
public static int Count(this IEnumerable source)
{
int res = 0;
foreach (var item in source)
res++;
return res;
}
}
【讨论】:
ICollection 的答案更好,因为它值得...
.Cast<object>().Count() 已经进行了检查(通用和非通用)。但是,是的,第二种解决方案可以通过检查来改进。