Список С# <T> против IEnumerable <T> вопрос производительности

Привет, предположим, что эти 2 метода:

private List<IObjectProvider> GetProviderForType(Type type)
        {
            List<IObjectProvider> returnValue = new List<IObjectProvider>();

            foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
            {
                if ((provider.Key.IsAssignableFrom(type) ||
                    type.IsAssignableFrom(provider.Key)) &&
                    provider.Value.SupportsType(type))
                {
                    returnValue.Add(provider.Value);
                }
            }
            return returnValue;
        }

private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
        {
            foreach (KeyValuePair<Type, IObjectProvider> provider in _objectProviders)
                if ((provider.Key.IsAssignableFrom(type) ||
                    type.IsAssignableFrom(provider.Key)) &&
                    provider.Value.SupportsType(type))

                    yield return provider.Value;              
        }

Какой из них быстрее? Когда я смотрю на первый метод, я вижу, что память выделена для List, что, на мой взгляд, не нужно. Метод IEnumerable кажется мне быстрее.

Например, предположим, что вы вызываете

int a = GetProviderForType(myType).Count;
int b = GetProviderForType1(myType).Count();

Теперь еще одна проблема: существует ли разница в производительности между этими двумя выше?

Как вы думаете?

Ответы

Ответ 1

В этом конкретном случае использование формы IEnumerable<T> будет более эффективным, потому что вам нужно знать только счет. Нет смысла хранить данные, изменять размеры буферов и т.д., Если вам это не нужно.

Если вам нужно было снова использовать результаты по какой-либо причине, форма List<T> была бы более эффективной.

Обратите внимание, что как метод расширения Count(), так и свойство Count будут эффективны для List<T>, поскольку реализация Count() проверяет, реализует ли целевая последовательность ICollection<T> и использует свойство Count если да.

Другой вариант, который должен быть еще более эффективным (хотя и только), - вызвать перегрузку Count, которая принимает делегат:

private int GetProviderCount(Type type)
{
  return _objectProviders.Count(provider =>
      (provider.Key.IsAssignableFrom(type) 
       || type.IsAssignableFrom(provider.Key))
      && provider.Value.SupportsType(type));
}

Это позволит избежать дополнительного уровня ограничений, вызванных предложениями Where и Select.

(Как говорит Марк, для небольших объемов данных различия в производительности, вероятно, будут незначительными в любом случае.)

Ответ 2

Точный ответ на такие вопросы может варьироваться в зависимости от множества факторов и может измениться по мере развития CLR. Единственный способ убедиться в том, чтобы измерить его - и имейте в виду, что если разница невелика по сравнению с операцией, в которой она появится, тогда вы должны выбрать наиболее читаемый, удобный для пользователя способ записи.

И в этом примечании вы также можете попробовать:

private IEnumerable<IObjectProvider> GetProviderForType1(Type type)
{
    return _objectProviders.Where(provider => 
                  provider.Key.IsAssignableFrom(type) ||
                  type.IsAssignableFrom(provider.Key)) &&
                  provider.Value.SupportsType(type))
                           .Select(p => p.Value);
}

Вы также можете предоставить себе большую гибкость, возвращая IEnumerable<T>, а затем используя метод расширения ToList, если вы хотите "сделать снимок" результатов в списке. Это позволит избежать повторной оценки кода для генерации списка, если вам нужно его несколько раз проверять.

Ответ 3

Важной частью этого вопроса является "насколько велики данные"? Сколько строк...

Для небольших количеств данных список в порядке: для распределения достаточно большого списка потребуется незначительное время, и он не будет изменять размер много раз (ни один, если вы можете сказать, насколько велика будет заранее).

Однако это не масштабируется для огромных объемов данных; маловероятно, что ваш провайдер поддерживает тысячи интерфейсов, поэтому я бы не сказал, что это необходимо, чтобы перейти к этой модели, но это не повредит.

Конечно, вы также можете использовать LINQ:

return from provider in _objectProviders
       where provider.Key.IsAssignableFrom(type) ...
       select provider.Value;

Это также отложенный подход yield под обложками...

Ответ 4

Основная разница между IEnumerable и IList:

IEnumerable: Реализовывает MoveNext, Reset, возвращает текущие методы и возвращает тип IEnumerator для Iterate   Через записи.

IList: предоставляет интерфейс IEnumerable, а также представляет собой набор не общих объектов, к которым можно получить доступ через индекс, поэтому IEnumerable + ICollection (манипулирование данными) и добавление, удаление, вставка (по конкретному индексу) являются полезными методы, реализованные IList.

После просмотра кода в моем мнении IEnumerable более эффективен, но возвращаемый список также полезен, если вы хотите сделать некоторые манипуляции с данными, и если вы просто хотите перебирать данные, тогда IEnumerable предпочтительнее.