Расчет подсчета для IEnumerable (Non Generic)

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

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

Ответ 1

Простейшая форма:

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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