Список C # <T>против IEnumerable <T>вопрос производительности - PullRequest
15 голосов
/ 31 июля 2009

Привет, предположим, эти 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();

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

Что вы думаете?

Ответы [ 4 ]

31 голосов
/ 31 июля 2009

В этом конкретном случае использование формы 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.

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

4 голосов
/ 31 июля 2009

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

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

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

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

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

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

4 голосов
/ 31 июля 2009

Точный ответ на подобные вопросы может варьироваться в зависимости от множества факторов и может меняться по мере развития 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, если хотите «сделать снимок» результатов в списке. Это позволит избежать повторной оценки кода для создания списка, если вам нужно будет просмотреть его несколько раз.

2 голосов
/ 22 октября 2011

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

IEnumerable: Реализует MoveNext, Reset, Get Current Methods и возвращает тип IEnumerator для итерации Сквозные записи.

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

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

...