Расчет количества для IEnumerable (не универсальный) - PullRequest
46 голосов
/ 09 апреля 2011

Может ли кто-нибудь помочь мне с Count методом расширения для IEnumerable (не универсальный интерфейс).

Я знаю, что это не поддерживается в LINQ, но как написать это вручную?

Ответы [ 4 ]

47 голосов
/ 09 апреля 2011
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;
    }
}
42 голосов
/ 09 апреля 2011

Самая простая форма будет:

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;
}

Обновление

Как отмечает Джерард в комментариях, неуниверсальный 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;
}
4 голосов
/ 09 апреля 2011

Различные типы IEnumerable имеют разные оптимальные методы определения количества; к сожалению, нет универсальных средств для определения того, какой метод будет наилучшим для любого данного IEnumerable, и нет даже никаких стандартных средств, с помощью которых IEmumerable может указать, какой из следующих методов является лучшим:

  1. Просто спросите объект напрямую. Некоторые типы объектов, которые поддерживают IEnumerable, такие как Array, List и Collection, имеют свойства, которые могут напрямую сообщать о количестве элементов в них.
  2. Перечислите все предметы, отбрасывая их, и подсчитайте количество перечисленных предметов.
  3. Перечислить все элементы в список, а затем использовать список, если необходимо снова использовать перечисление.

Каждое из вышеперечисленных будет оптимальным в разных случаях.

3 голосов
/ 17 ноября 2015

Я думаю, что тип, выбранный для представления вашей последовательности элементов, должен был быть ICollection вместо IEnumerable , во-первых.

И ICollection, и ICollection<T> предоставляют свойство Count - плюс - каждая ICollection также реализует IEnumearable.

...