Список С# <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 предпочтительнее.