LinqとCountの効率
- 2009-07-21
IEnumerableを受け取ってのReverse(), Count(), ElementAt(), Last()は普通に考えると先頭から舐めるので効率がよろしくない。じゃあLinqの実装はどうなってるかというとSystem.Core.dllを眺めると
// Reverse
ICollection<TElement> is2 = source as ICollection<TElement>;
// ElementAt
IList<TSource> list = source as IList<TSource>;
// Count
ICollection<TSource> is2 = source as ICollection<TSource>;
// Last
IList<TSource> list = source as IList<TSource>;
というわけで、IListを実装しているものなら、ちゃんと変換してくれているので大丈夫。Count()なんかは普通にLength/Countを使うから別にどうでもいいって話なのですが、Last()は[hoge.Length - 1]って書くのは嫌なので、こうして安心して使えると嬉しい話。まあ、こんなことは過去何回も話題に上っているのですが、一応自分で確認しておかないとね、ということで。
MSDNのCountの説明にはICollectionを実装してるとそれ使う、って書いてあるけど、Lastのページには何も書いていなくて困ります。内部でisだのasだのやってゴニョゴニョしてるのなら、そのことも全部記載して欲しいなあ。
といったことを何故突然というと、C#にもほしい ~rubyのeach_with_index~ - SEの勉強日記という記事を見かけたので。Count()のうえにElementAt()のラッシュというのは、IListだけならいいんですけどIEnumerableで拡張メソッドを作っているので、少しよろしくない。
public static class Extension
{
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
}
public static void ForEach<T>(this IEnumerable<T> source, Action<T, int> action)
{
var index = 0;
foreach (var item in source)
{
action(item, index++);
}
}
}
class Program
{
static void Main(string[] args)
{
Enumerable.Range(5, 6).ForEach((item, index) =>
Console.WriteLine("item={0} index={1}", item, index));
}
}
という風にしたほうがいいと思われます。まあ、あと、new List()なんてやるぐらいなら.ToList()かしらん。