Подтвердить что ты не робот

Какова роль IEnumerable <T> и почему я должен ее использовать?

Зачем мне использовать IEnumerable<T>, когда я могу обойтись... скажем List<T>? Какое преимущество первого над последним?

4b9b3361

Ответ 1

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

List<T>, с другой стороны, представляет собой конкретную реализацию IEnumerable<T>, которая сохраняет объекты определенным, известным образом. Внутренне это может быть очень хорошим способом хранения ваших значений, которые вы открываете с помощью IEnumerable<T>, но List<T> не всегда подходит. Например, если вам не нужно обращаться к элементам по индексу, но постоянно вставляйте элементы в начале вашей коллекции, а затем удаляйте элементы из конца, более подходящим будет использовать Queue<T>.

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

Ответ 2

В этот момент Джеффри-Рихтер пишет:

При объявлении типов параметров методов вы должны указать самый слабый тип, предпочитая интерфейсы по базовым классам. Например, если вы пишете метод, который манипулирует набором элементов, было бы лучше объявить параметр методов используя интерфейс, например IEnumerable<T>, вместо использования сильного типа данных, такого как List<T>, или даже более сильный тип интерфейса, например ICollection<T> или IList<T>:

// Desired: This method uses a weak parameter type   
public void ManipulateItems<T>(IEnumerable<T> collection) { ... }  

// Undesired: This method uses a strong parameter type   
public void ManipulateItems<T>(List<T> collection) { ... }

Причина, конечно, в том, что кто-то может вызвать первый метод, проходящий в объекте массива, объект List<T>, объект String и т.д. - любой объект, тип которого реализует IEnumerable<T>. Второй метод позволяет передавать только объекты List<T>; он не примет массив или объект String. Очевидно, что первый метод лучше, потому что он гораздо более гибкий и может использоваться в гораздо более широком диапазоне сценариев.

Естественно, если вы пишете метод, требующий списка (а не только любого перечислимого объекта), тогда вы должны объявить тип параметра как IList<T>. Вы все равно должны избегать объявления типа параметра как List<T>. Использование IList<T> позволяет вызывающему агенту передавать массивы и любые другие объекты, тип которых реализует IList<T>.

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

Ответ 3

Различные реализации коллекций можно перечислить; использование IEnumerable дает понять, что вас интересует перечислимость, а не структура базовой реализации коллекции.

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

Ответ 4

Используя концепцию итераторов, вы можете добиться значительного улучшения качества алгоритма, как с точки зрения скорости, так и использования памяти.

Рассмотрим два примера кода. Оба анализируют файл, один хранит строки в коллекции, другой - перечислимый.

Первый пример принимает время C * O (N) и память O (N)

IEnumerable<string> lines = SelectLines();
List<Item> items = lines.Select(l=>ParseToItem(l)).ToList();
var itemOfIterest = items.FirstOrDefault(IsItemOfIterest); 

Второй пример принимает 1/2 C * O (N) время и постоянную память.

var itemOfIterest = lines.FirstOrDefault(l=>IsItemOfIterest(ParseToItem(l));

Вот код SelectLines()

 IEnumerable<string> SelectLines()
 {
  ...
  using(var reader = ...)
  while((line=reader.ReadLine())!=null)
   yield return line;
 }

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

Конечно, в списке будут храниться все элементы в памяти, поэтому использование памяти O (N).

Ответ 5

Обычно вы не используете IEunumerable напрямую. Это базовый класс для ряда других коллекций, которые вы, скорее всего, будете использовать. IEnumerable, например, обеспечивает возможность прокрутки коллекции с помощью foreach. Это используется многими наследующими классами, такими как List<T>. Но IEnumerable не предлагает метод сортировки (хотя вы можете использовать Linq для этого), а некоторые другие общие коллекции, такие как List<T>, имеют этот метод.

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

Ответ 6

Если вы планируете создавать открытый API, лучше использовать IEnumerable, чем List<T>, потому что вам лучше использовать минималистичный интерфейс/класс. List<T> позволяет вам обращаться к объектам по индексу, если это необходимо.

Здесь является довольно хорошим ориентиром, когда следует использовать IEnumerable, ICollection, List<T> и т.д.

Ответ 7

IEnumerable предоставляет вам способ реализовать свою собственную логику хранения и итерации по коллекции объектов

Ответ 8

Реализация IEnumerable <T> обычно является предпочтительным способом для класса указывать, что он должен использоваться с циклом "foreach" и что несколько циклов "foreach" на одном и том же объекте должны работать независимо. Хотя есть использование IEnumerable <T> кроме "foreach" , нормальное указание на то, что нужно реализовать IEnumerable, состоит в том, что класс является таким, где имеет смысл сказать "foreach foo в classItem {foo.do_something();}.

Ответ 9

  • IEnumerable использует отложенное выполнение, как описано здесь: IEnumerable vs List - что использовать? Как они работают?

  • IEnumerable разрешает неявное преобразование ссылок для типов массивов, которые известны как ковариация. Рассмотрим следующий пример:

публичный абстрактный класс Автомобиль { }

public class Car :Vehicle
{
}

private void doSomething1(IEnumerable<Vehicle> vehicles)
{

}

private void doSomething2(List<Vehicle> vehicles)
{

}

var vec = new List<Car>();
doSomething1(vec); // this is ok 
doSomething2(vec); // this will give a compilation error