Я обычно делаю что-то как:
string[] things = arrayReturningMethod();
int index = things.ToList<string>.FindIndex((s) => s.Equals("FOO"));
//do something with index
return things.Distinct(); //which returns an IEnumerable<string>
и я нахожу всю эту путаницу типов/интерфейса немного сбивающей с толку, и она щекочет мои потенциальные проблемные антенны производительности (который я игнорирую, пока не доказано свою правоту, конечно).
Этот идиоматический и надлежащий C# или является там лучшей альтернативой, чтобы не вспоминать и дальше получать доступ к правильным методам работать с данными?
Править: Вопрос является на самом деле двукратным:
Когда следует использовать или интерфейс IEnumerable или массив или список (или какой-либо другой IEnumerable, реализующий тип) непосредственно (при принятии параметров)?
Необходимо ли свободно переместиться между IEnumerables (неизвестная реализация) и списками и IEnumerables и массивами и массивами и Списками или разве который не идиоматичен (существуют лучшие способы сделать это) / не производительный (не обычно релевантный, но мог бы быть в некоторых случаях) / просто ужасный (unmaintable, нечитабельный)?
Что касается производительности ...
Что касается идиом ...
В общем, IEnumerable полезен для общедоступных свойств, параметров функций и часто для возвращаемых значений - и только если вы знаете, что собираетесь использовать значения последовательно.
Например, если бы у вас была функция PrintValues, если бы она была записана как PrintValues (List
Что касается внутреннего использования ...
Также обратите внимание, что приведение типов отличается от использования ToArray () или ToList () - последнее включает в себя копирование значений, что действительно снижает производительность и память, если у вас много элементов. Первое - это просто сказать, что «Собака - это животное, поэтому, как и любое животное, оно может есть» (подавленное) или «Это животное оказывается собакой, поэтому оно может лаять» (обращенное вверх). Аналогично, All Lists и T [] являются IEnumerables, но только некоторые IEnumerables являются Lists или T [] s.
Мне кажется, проблема в том, что вы не удосужились научиться искать в массиве. Подсказка: Array.IndexOf или Array.BinarySearch в зависимости от того, отсортирован ли массив.
Вы правы в том, что преобразование в список - плохая идея: оно тратит впустую пространство и время и делает код менее читаемым. Кроме того, слепое преобразование в IEnumerable
замедляет работу, а также полностью предотвращает использование определенных алгоритмов (таких как двоичный поиск).
Итак, у вас есть два яблока и апельсин, которые вы сравниваете.
Два яблока - это массив и список.
Массив в C # - это массив в стиле C, в который встроена сборка мусора. Плюс их использования в том, что у них очень мало накладных расходов, при условии, что вам не нужно что-то перемещать. Плохо то, что они не так эффективны, когда вы добавляете, удаляете или иным образом меняете массив, поскольку память перетасовывается.
Список - это динамический массив в стиле C # (похожий на класс vector <> в C ++). Это больше накладных расходов, но они более эффективны, когда вам нужно много перемещать вещи, поскольку они не будут пытаться сохранить непрерывное использование памяти.
Лучшее сравнение, которое я мог бы дать, - это сказать, что массивы относятся к спискам, как строки - к StringBuilders.
Оранжевый - это IEnumerable. Это не тип данных, а скорее интерфейс. Когда класс реализует интерфейс IEnumerable, он позволяет использовать этот объект в цикле foreach ().
Когда вы возвращаете список (как в своем примере), вы не преобразовывали список в IEnumerable. Список уже является объектом IEnumerable.
РЕДАКТИРОВАТЬ: Когда преобразовывать между двумя:
Это зависит от приложения.С массивом можно сделать очень мало, чего нельзя сделать с помощью List, поэтому я обычно рекомендую List. Вероятно, лучше всего принять решение о том, что вы собираетесь использовать одно или другое, чтобы вам не приходилось переключаться между ними. Если вы полагаетесь на внешнюю библиотеку, абстрагируйте ее, чтобы обеспечить последовательное использование.
Надеюсь, это немного рассеяло туман.
Когда что использовать?
Я бы посоветовал вернуть наиболее конкретный тип и выбрать наиболее гибкий тип.
Примерно так:
public int[] DoSomething(IEnumerable<int> inputs)
{
//...
}
public List<int> DoSomethingElse(IList<int> inputs)
{
//...
}
Таким образом, вы можете вызывать методы в List
для всего, что вы получите от метода, в дополнение к обработке его как IEnumerable. На входах используйте максимально гибкие возможности, чтобы вы не диктовали пользователям вашего метода, какую коллекцию создавать.
Я стараюсь избегать быстрого перехода между типами данных, если этого можно избежать.
Должно быть так, что каждая ситуация, подобная описанной вами, достаточно отличается, чтобы не допустить догматического правила о преобразовании ваших типов; однако, как правило, хорошей практикой является выбор структуры данных, которая обеспечивает как можно лучший интерфейс, который вам нужен, без необходимости без необходимости копировать элементы в новые структуры данных.
Хорошее практическое правило - всегда использовать IEnumerable (при объявлении ваших переменных / параметров метода / типов возвращаемых значений метода / свойств / и т. Д.), Если у вас нет веской причины не делать. Безусловно, наиболее совместим по типу с другими методами (особенно с расширениями).
Вы правы, игнорируя антенны «проблемы с производительностью», пока у вас действительно не возникнет проблема с производительностью. Большинство проблем с производительностью возникает из-за выполнения слишком большого количества операций ввода-вывода или слишком большого количества блокировок или неправильного выполнения одного из них, и ни один из этих факторов не относится к этому вопросу.
Мой общий подход:
Следствие №4: не бойтесь ToList (). ToList () - ваш друг. Это заставляет IEnumerable
Конечно, это всего лишь приблизительная рекомендация. Просто попробуйте следовать тому же шаблону в той же кодовой базе - скачкообразные стили кода усложняют для программистов техобслуживания понимание вашего настроения.