Enumerable.Count()メソッドの特徴を考えてみる。ただ要素数を数えるだけなら、以下のような実装で良い筈だ。
public static class LinqExtensions { public static int Count<T>(this IEnumerable<T> enumerable) { var count = 0; foreach (var value in enumerable) { count++; } return count; } }
当たり前だが、これでは要素を全て列挙しなければ個数が分からない。例えば:
// 100000000個の値が入ったリスト var values = Enumerable.Range(0, 100000000).ToList(); // 有意に時間がかかる var count1 = LinqExtensions.Count(values); // こちらは一瞬 var count2 = Enumerable.Count(values);
何故、Enumerable.Count()は速いのだろうか。それは、列挙子の実行時の型を見て最適化を行っているからだ。ICollectionインターフェイスを実装しているクラスであれば、Countプロパティを参照するだけで個数が識別出来る。この方法を利用するには、以下のようにキャストで判定する。
public static class LinqExtensions { public static int Count<T>(this IEnumerable<T> enumerable) { // 列挙子がICollectionを実装していれば var collection = enumerable as ICollection; if (collection != null) { // 直接Countプロパティを参照する return collection.Count; } var count = 0; foreach (var value in enumerable) { count++; } return count; } }
列挙子として配列を使用する場合、配列の長さを調べるにはLengthプロパティを参照する。しかし、配列は暗黙にICollectionインターフェイスを実装しているため、わざわざ配列かどうかを確認しなくても、上記コードがそのまま使える。