Массив по сравнению со списком <T>: когда использовать?

MyClass[] array;
List<MyClass> list;

Каковы сценарии, когда один предпочтительнее другого? И почему?

Массивы довольно устарели, как видно из популярное обсуждение здесь. Также указал здесь и нашим хостом в блоге.

gimel 12.01.2009 11:14

Если я не ошибаюсь, у List <> есть массив как внутренняя структура. Всякий раз, когда внутренний массив заполняется, он просто копирует содержимое в массив, размер которого вдвое больше (или на некоторую другую константу, умноженную на текущий размер). en.wikipedia.org/wiki/Dynamic_array

Ykok 12.09.2013 16:15

Ыкок: То, что вы говорите, кажется правильным, я нашел исходный код Список <> здесь.

Carol 22.11.2014 09:20

@gimel Утверждать, что массивы устарели, возможно, немного смело

awdz9nld 25.11.2015 18:08
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
651
4
324 563
15
Перейти к ответу Данный вопрос помечен как решенный

Ответы 15

Ответ принят как подходящий

На самом деле, вы редко можете захотеть использовать массив. Определенно используйте List<T> в любое время, когда вы хотите добавить / удалить данные, поскольку изменение размера массивов дорого. Если вы знаете, что данные имеют фиксированную длину, и хотите выполнить микрооптимизацию по какой-либо причине очень специфический (после тестирования), тогда может быть полезен массив.

List<T> предлагает много больше функциональных возможностей, чем массив (хотя LINQ немного выравнивает его) и почти всегда является правильным выбором. За исключением, конечно, аргументов params. ;-п

В качестве фишки - List<T> одномерный; где-как у вас есть прямоугольные (и т. д.) массивы, такие как int[,] или string[,,], но есть и другие способы моделирования таких данных (если вам нужно) в объектной модели.

Смотрите также:

Тем не менее, я делаю много использования массивов в моем проекте protobuf-net; полностью для исполнения:

  • он выполняет много битового сдвига, поэтому byte[] очень важен для кодирования;
  • Я использую локальный скользящий буфер byte[], который заполняю перед отправкой в ​​базовый поток (и v.v.); быстрее, чем BufferedStream и т. д .;
  • он внутренне использует модель объектов на основе массивов (Foo[], а не List<Foo>), поскольку размер фиксируется после построения и должен быть очень быстрым.

Но это определенно исключение; в области обработки данных общего назначения List<T> всегда выигрывает.

Аргумент об изменении размера абсолютно верен. Однако люди предпочитают списки, даже если изменение размера не требуется. В этом последнем случае есть веский, логичный аргумент или это не что иное, как «массивы вышли из моды»?

Frederick The Fool 12.01.2009 11:25

@Frederick: трудно ответить, не услышав «почему». На самом деле, во многих случаях я бы предпочел IList<T> или IEnumerable<T> - это позволяет вызывающему абоненту решать, что они хотят использовать (T[] или List<T> и т. д.). LINQ в значительной степени уравновешивает множество дополнительных функций List<T> (по сравнению с IList<T>).

Marc Gravell 12.01.2009 11:28

На самом деле я работаю с устаревшей кодовой базой C# 1.0. Некоторые функции принимают и возвращают массивы. Никакого изменения размера и ничего не происходит ....

Frederick The Fool 12.01.2009 11:37

... Теперь в своих функциях-оболочках я склонен использовать List <T> вместо массивов. Но у меня нет веских причин, чтобы это оправдать. Просто теплое нечеткое ощущение. Мне нужен веский аргумент для моего решения.

Frederick The Fool 12.01.2009 11:38

Что ж, без дженериков все немного по-другому ... Бокс и кастинг - это не вещи худший в мире, но они складываются. Я бы сказал, что у массивов немного больше места в .NET 1.x, чем сейчас, но есть еще ArrayList и т. д., Если вы можете жить с болью, связанной с box / cast / no-compile-time-check.

Marc Gravell 12.01.2009 11:40

@Frederick - серьезно: рассмотрите IList <T> (если вам нужно добавить / удалить / индексатор) или IEnumerable <T> (если вам просто нужен foreach).

Marc Gravell 12.01.2009 11:41

Я не совсем понимаю: нужно ли для работы с массивом упаковка / распаковка? Даже если тип, содержащийся в массиве, является ссылочным типом? А кастинг тоже нужен? Но разве массив не строго типизирован? PS: Мне просто любопытно, есть ли какая-то догма против массивов. PPS: Еще не прочитал ссылку Джона Скита.

Frederick The Fool 12.01.2009 11:49

Ага. Я ценю и согласен с использованием IList <T> и IEnumerable <T>. Они делают пользователя вашего класса менее ограниченным с точки зрения того, что ему предоставлять и что ожидать от вашего класса.

Frederick The Fool 12.01.2009 11:51

@Frederick - мои комментарии к боксу / касту были направлены на ArrayList и т. д., Поскольку они являются ближайшим вариантом к List <T> в .NET 1.x

Marc Gravell 12.01.2009 11:55

«Определенно используйте List <T> всякий раз, когда вы хотите добавить / удалить данные, поскольку изменение размера массивов дорогое удовольствие». List <T> использует массив внутри. Вы думали о LinkedList <T>?

dan-gph 09.03.2010 09:49

@Dangph: List <T> сохраняет массив больше, чем он должен быть, поэтому не каждая операция добавления / удаления требует изменения размера массива, поскольку большую часть времени в конце массива все еще остается свободное место.

Lucas Werkmeister 17.10.2013 19:04

@lucas, а что, если убрать элемент из середины массива? Все элементы над ним нужно будет перетасовать, чтобы заполнить дыру.

dan-gph 18.10.2013 07:17
Except for params arguments, of course ;-p Почему кроме аргументов params? Не могли бы вы немного объяснить?
Gaui 04.11.2013 15:48

@Gaui, потому что paramsТолько существует для векторов; так что это одно из мест, где естественнее использовать голый массив. Конечно, лично я очень противник невидимого распределения, поэтому в первую очередь стараюсь избегать params.

Marc Gravell 04.11.2013 15:53

Вы писали: «Есть и другие способы моделирования таких [n-мерных] данных (если вам нужно) в объектной модели». Что ж, мне действительно нужно, поскольку приложение, с которым я работаю, интенсивно использует n-мерные данные, и я с удовольствием использую double[,], Point3D[,] и тому подобное. Есть ли у вас какие-либо ресурсы, чтобы указать подходящие объектные модели для этого использования, которые лучше, чем простые массивы? Мои массивы обычно плотные и не меняют размер и форму. Спасибо!

heltonbiker 11.06.2014 23:51

Больше функций == более сложные == не годятся, если вам не нужны эти функции. Этот ответ в основном перечисляет причины, по которым массивы лучше, но делает противоположный вывод.

Eamon Nerbonne 20.06.2014 13:35

@ lucas.werkmeister Да. Ничто не мешает вам удвоить размер массива, когда он сам заполняется, поэтому затраты на изменение размера массива не имеют значения.

Casey 18.07.2014 01:17

@EamonNerbonne Я не совсем согласен с этим. Большинство «функций», о которых вы говорите (например, реализация динамических массивов), не похожи на новомодный мусор, которым вы вряд ли когда-нибудь воспользуетесь.

Casey 18.07.2014 01:19

@emodendroket Неважно, новомодные ли они, дополнительные сложности вам просто не нужны. Вы действительно используете эти функции? Нет? Не используйте List<>. Поскольку структуры данных, которые меняются со временем, затрудняют понимание кода, я настоятельно рекомендую вам не использовать List<>.Add, если вы не уделили этому внимание; array просто удаляют еще один источник ошибок в коде, который не требует динамического изменения размера (т.е. большую часть кода).

Eamon Nerbonne 18.07.2014 15:48

@EamonNerbonne, если вы не используете эти функции, я могу в значительной степени гарантировать, что они не причинят вам вреда ... но: количество коллекций, которые никогда не нуждаются в мутации, на много меньше, по моему опыту, чем те, которые мутировавший

Marc Gravell 18.07.2014 15:52

@MarcGravell: это зависит от вашего стиля кодирования. По моему опыту, практически ни одна коллекция не видоизменяется. Это; коллекции извлекаются из базы данных или создаются из какого-либо источника, но дальнейшая обработка всегда выполняется путем воссоздания новой коллекции (например, карты / фильтра и т. д.). Даже если концептуальная мутация необходима, проще всего просто создать новую коллекцию. Я изменяю коллекцию только для оптимизации производительности, и такая оптимизация, как правило, носит очень локальный характер и не раскрывает мутацию потребителям API.

Eamon Nerbonne 18.07.2014 16:38

@MarcGravell: для некоторого контекста, неизменяемые коллекции - это явно то, в чем LINQ лучше всего, и в наши дни это не редкость. Как странно «видоизменить» результат запроса linq (почему вы изначально не правильно выбрали + project?), Так и концептуально сложно изменить источник запроса linq из-за смеси нетерпеливого и ленивого оценка. Если вы создаете веб-сайт с поддержкой базы данных, мне было бы любопытно, есть ли вообще какой-либо (общий) вариант использования изменяемых коллекций - конечно, за пределами самой базы данных.

Eamon Nerbonne 18.07.2014 16:47

@EamonNerbonne Я не могу не согласиться с тем, что «большая часть кода» не требует изменения размера массива, и есть много других вещей, о которых вам нужно беспокоиться, если вы работаете с массивами.

Casey 18.07.2014 17:48

@emodendroket: некоторый код работает с коллекциями путем ручного зацикливания и обработки структур данных по мере их поступления. Этому коду нравятся foreach и List<>.Add. Другой код обрабатывает коллекции как значения и вычисляет новые коллекции из старых. Такой код похож на LINQ и мало пригоден для изменяемых коллекций. Этот ответ неявно предпочитает циклы + мутацию функциональным конструкциям, и я не думаю, что такое предпочтение должно быть неявно заявлено как факт, каким оно есть. Большая часть кода, который я вижу, имеет тенденцию к функциональности - вычислению новых коллекций (или итераторов) из существующих неизмененных данных.

Eamon Nerbonne 18.07.2014 18:52

@MarcGravell: изменчивость List<> ухудшает на практике именно потому, что некоторый код традиционно ожидает изменений, выполняемых мутацией. Избегая метода Add, вы сразу же избегаете первого источника случайного неправильного использования, и по опыту работы с большим проектом я могу сказать, что такие ошибки неприятны, потому что (как и ошибки null-deref) код, который дает сбой или вычисляет неправильное значение, часто далеко от источника ошибки (код, который изменил внутреннюю структуру данных, которая, как предполагалось, не изменилась). TL; DR: Если вы идете по маршруту LINQ, каков вариант использования List<>?

Eamon Nerbonne 18.07.2014 18:57

@EamonNerbonne Если вам нужны неизменяемые коллекции, вам следует использовать неизменяемую коллекцию. Массивы изменяемы; они просто фиксированы по длине.

Casey 18.07.2014 20:51

@EamonNerbonne Массивы - лучший выбор в довольно редких обстоятельствах, когда вы нужно изменяемую коллекцию, но имеющую фиксированную длину. Это вообще не функциональное программирование.

Casey 18.07.2014 20:52

@emodendroket: Массивы не идеальны, но они лучше, чем List<>, если вам не нужна изменчивость: они быстрее, используют меньше памяти и избегают наиболее распространенных изменяемых операций. К сожалению, .NET не предоставляет сопоставимый неизменяемый массив (есть пакет nuget для System.Collections.Immutable, но реализация массива все еще является предварительным выпуском), и даже после того, как это произойдет, не все могут немедленно обновить .NET до последней версии и обертка неизменяемости будет иметь некоторые накладные расходы. В общем: прямо сейчас Array достаточно хорош (и, безусловно, лучше, чем немутантные варианты использования List<>.

Eamon Nerbonne 21.07.2014 16:39

@emodendroket: подумайте об этом так: именно потому, что List<> настолько подходит для мутирующих коллекций, он не подходит для немутантных коллекций, потому что тип передает неправильное сообщение.

Eamon Nerbonne 21.07.2014 16:40

@EamonNerbonne Что ж, использование ReadOnlyCollection<T> пошлет это сообщение еще сильнее.

Casey 22.07.2014 00:49

@emodendroket: ReadOnlyCollection<T> более подробный, а ужасающе медленный. Для публичного API - конечно. Но большая часть кода не находится в общедоступном API, и соглашения о кодировании, которые гласят, что «не вмешивайтесь в содержимое массива после построения», работают нормально. Обеспечение неизменности не является естественным или легким делом в C#, и я не думаю, что дополнительные умственные издержки и время выполнения того стоят, особенно когда простые соглашения почти так же хороши, намного проще и быстрее.

Eamon Nerbonne 22.07.2014 11:08

почему вы говорите: «List <T> одномерный; где-как у вас есть прямоугольные (и т. д.) массивы» <--- Действительно, вы можете иметь прямоугольные или зубчатые массивы, но точно так же вы можете иметь List <List <string >> Так зачем предполагать, что List является 1D, если он не больше или меньше 1D, чем массивы?

barlop 23.09.2016 08:02

@barlop k; int[,] - это явно 2D-массив. int[,,] явно является трехмерным массивом. int[][], однако, не является «двумерным массивом» - это одномерный массив одномерных массивов, также известный как зубчатый массив. Точно так же List<List<int>> не является двухмерным списком: это список списков. Дело в том, что в случае int[,] есть один массив, поэтому мы можем описать свойства этого 1 массива. В случае List<List<int>> имеется n + 1 список; one - это одномерный список списков, когда - одномерные списки целых чисел. Да, вы можете использовать List<List<int>> для выражения 2D-данных, но это не одно и то же. Семантика имеет значение.

Marc Gravell 23.09.2016 09:53

@MarcGravell А как насчет переключения между списками и массивами, когда этого требует случай. то есть, как только список будет фиксированным по размеру, переключитесь на массив и выполните что-то в массиве, и, возможно, если нам нужно снова широко добавить / удалить, снова переключитесь обратно на списки (используя LINQ)

tfrascaroli 29.08.2017 15:11

@tfrascaroli вы не можете просто переключаться между массивами и списками, не оплачивая распределение + затраты на копирование, что сделало бы любой такой подход чрезмерно дорогим

Marc Gravell 29.08.2017 23:11

@MarcGravell да, я понял это. Дело в том, что если список дешевле добавлять / удалять элементы, а затем массив дешевле читать и использовать, возможно, преобразование сразу после того, как мы закончили добавление, было бы не так уж плохо. Но я согласен с тем, что такой подход выглядит пустой тратой ресурсов. Я не знаю, как преобразования в LINQ реализуются внутри, хотя ...

tfrascaroli 29.08.2017 23:39

Массивы для вариативного params или для гарантии фиксированного размера. Список для изменения размера.

solstice333 31.01.2019 14:59

Кроме того, некоторые API просто естественно возвращают массив, например, Regex.split

solstice333 31.01.2019 15:14

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

Tony Cheetham 13.05.2020 14:05

@TonyCheetham, это может быть правдой только в том случае, если вы занимаетесь списками / массивами типов значений, а не ссылочными типами

Cesar 10.07.2020 14:59

Если вы действительно не озабочены производительностью, и под этим я имею в виду: «Почему вы используете .Net вместо C++?» вам следует придерживаться List <>. Его легче поддерживать, и он делает всю грязную работу по изменению размера массива за вас. (При необходимости List <> довольно умен в выборе размеров массива, поэтому обычно в этом нет необходимости.)

«Почему вы используете .Net вместо C++?» XNA

Bengt 12.06.2011 08:54

Чтобы уточнить комментарий @Bengt, производительность не является приоритетом .NET, и это, безусловно, справедливо. Но некоторые светлые умы подумали об использовании .NET с игровыми движками. Фактически, большинство игровых движков в настоящее время используют C#. В этом отношении Unity 3D - это парадигма «Когда игровой движок не предназначался для AAA».

mireazma 07.10.2020 08:25

@mireazma Это не было правильным утверждением (относительно Unity) в течение долгого времени. Мы получаем производительность C++ от C# через HPC# и Burst. Многие внутренние компоненты движка переносятся на C#. Даже если вы говорите об игровом сценарии в проекте, не относящемся к DOTS, IL2CPP выполняет очень и очень хорошую работу по созданию высокопроизводительного кода.

3Dave 19.02.2021 21:45

@ 3Dave Не будем начинать полемику по этому поводу. «IL2CPP делает очень, очень хорошую работу по созданию высокопроизводительного кода». Действительно, в меру досягаемости. В отличие от jacksondunstan.com/articles/3001 (в 2015 году) я думаю, что IL2CPP по сравнению с моно очень хорошо выполняет свою работу. Но по сравнению с моно. По сравнению с родным C++ это ерунда. Я провел сравнительный арифметический тест в Unity с il2cpp в выпущенный exe со всеми оптимизациями по сравнению с C++, и время составило 18745, 18487 мс и более в Unity 2020 против 189 мс в C++ (иногда ~ 36 мс). Проверь себя.

mireazma 21.02.2021 18:06

@mireazma Мой комментарий был нацелен на то, чтобы быть более сосредоточенным на Burst и HPC#, где мы видим, что производительность такая же хорошая (а в некоторых случаях и лучше), чем эквивалентный собственный код. Тем не менее, ваши контрольные цифры интересны. Я разберусь с этим. Ваше здоровье.

3Dave 21.02.2021 22:38

Это полностью зависит от контекстов, в которых требуется структура данных. Например, если вы создаете элементы, которые будут использоваться другими функциями или службами, использование List - идеальный способ для этого.

Теперь, если у вас есть список элементов, и вы просто хотите их отобразить, скажем, на массиве веб-страницы - это контейнер, который вам нужно использовать.

Если у вас есть список элементов, и вы просто хотите их отобразить, то что плохого в том, чтобы просто использовать тот список, который у вас уже есть? Что здесь может предложить массив?

Marc Gravell 12.01.2009 11:18

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

Marc Gravell 12.01.2009 11:47

Несмотря на другие ответы, рекомендующие List<T>, вы захотите использовать массивы при обработке:

  • данные растрового изображения
  • другие низкоуровневые структуры данных (например, сетевые протоколы)

Почему для сетевых протоколов? Разве вы не предпочли бы использовать здесь настраиваемые структуры и дать им специальный сериализатор или явный макет памяти? Более того, что говорит против использования здесь List<T>, а не массива байтов?

Konrad Rudolph 12.01.2009 11:28

@Konrad - ну, для начала, Stream.Read и Stream.Write работают с byte [], как и Encoding и т.д.

Marc Gravell 12.01.2009 11:37

На самом деле просто отвечаю, чтобы добавить ссылку, которая, к моему удивлению, еще не была упомянута: запись в блоге Эрика Липперта на «Массивы считаются несколько вредными».

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

Наконец-то добрался до того, чтобы прочитать это более 3 лет спустя, ха-ха. Хорошая статья тогда, хорошая статья сейчас. :)

Spencer Ruport 20.04.2012 23:36

Если я точно знаю, сколько элементов мне понадобится, скажем, мне нужно 5 элементов и только 5 элементов Когда-либо, тогда я использую массив. В противном случае я просто использую List <T>.

Почему бы вам не использовать List <T> в случае, если вы знаете количество элементов?

Oliver 25.04.2017 14:39

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

См. http://msdn.microsoft.com/en-us/library/ms379570(v=vs.80).aspx#datastructures20_1_topic5 для получения дополнительной информации о списках в C# или просто декомпилируйте System.Collections.Generic.List<T>.

Если вам нужны многомерные данные (например, с использованием матрицы или в графическом программировании), вы, вероятно, вместо этого выберете array.

Как всегда, если проблема с памятью или производительностью, измерьте это! В противном случае вы можете сделать ложные предположения о коде.

Привет, не могли бы вы объяснить, почему «Время поиска списка будет O (n)» верно? Насколько я знаю, List <T> использует массив за кулисами.

dragonfly 07.08.2012 10:54

@dragonfly, ты совершенно прав. Источник. В то время я предполагал, что в реализации используются указатели, но с тех пор я узнал иное. Из приведенной выше ссылки: «Получение значения этого свойства - операция O (1); установка свойства также является операцией O (1). '

Sune Rievers 07.08.2012 12:30

Другая ситуация, о которой еще не упоминалось, - это когда у вас будет большое количество элементов, каждый из которых состоит из фиксированного набора связанных, но независимых переменных, связанных вместе (например, координаты точки или вершины трехмерного треугольника). Массив структур открытых полей позволит эффективно изменять его элементы «на месте» - то, что невозможно с любыми другими типами коллекций. Поскольку массив структур последовательно хранит свои элементы в ОЗУ, последовательные обращения к элементам массива могут быть очень быстрыми. В ситуациях, когда коду потребуется выполнить множество последовательных проходов через массив, массив структур может превзойти по производительности массив или другую коллекцию ссылок на объекты класса в 2: 1; кроме того, возможность обновлять элементы на месте может позволить массиву структур превзойти любой другой вид коллекции структур.

Хотя размеры массивов нельзя изменять, нетрудно заставить код хранить ссылку на массив вместе с количеством используемых элементов и при необходимости заменять массив на более крупный. В качестве альтернативы можно было бы легко написать код для типа, который ведет себя так же, как List<T>, но раскрывает его резервное хранилище, что позволяет говорить либо MyPoints.Add(nextPoint);, либо MyPoints.Items[23].X += 5;. Обратите внимание, что последний не обязательно вызовет исключение, если код попытается получить доступ за пределами конца списка, но в противном случае использование было бы концептуально очень похоже на List<T>.

Вы описали List <>. Есть индексатор, поэтому вы можете напрямую обращаться к базовому массиву, а List <> сохранит размер за вас.

Carl 15.09.2017 10:58

@Carl: Учитывая, например, Point[] arr;, код может сказать, например arr[3].x+=q;. Используя, например, List<Point> list, вместо этого необходимо было бы сказать Point temp=list[3]; temp.x+=q; list[3]=temp;. Было бы полезно, если бы у List<T> был метод Update<TP>(int index, ActionByRefRef<T,TP> proc, ref TP params). и компиляторы могут превратить list[3].x+=q; в {list.Update(3, (ref int value, ref int param)=>value+=param, ref q);, но такой возможности нет.

supercat 15.09.2017 18:09

Хорошие новости. Оно работает. list[0].X += 3; добавит 3 к свойству X первого элемента списка. И list - это List<Point>, а Point - это класс со свойствами X и Y

Carl 17.09.2017 04:53

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

Например,

var str = "This is a string";
var strChars = str.ToCharArray();  // returns array

Ясно, что модификация "strChars" не будет изменять исходный объект "str", независимо от уровня реализации базового типа "str".

Но предположим, что

var str = "This is a string";
var strChars = str.ToCharList();  // returns List<char>
strChars.Insert(0, 'X');

В этом случае только из этого фрагмента кода не ясно, будет ли метод вставки изменять исходный объект "str" ​​или нет. Чтобы сделать это определение, требуется знание String на уровне реализации, что нарушает подход «Дизайн по контракту». В случае String это не имеет большого значения, но может иметь большое значение почти во всех остальных случаях. Установка списка только для чтения помогает, но приводит к ошибкам во время выполнения, а не во время компиляции.

Я относительно новичок в C#, но мне непонятно, почему возврат списка предполагает изменчивость исходных данных так, как возврат массива - нет. Я бы хотел, чтобы метод, имя которого начинается с To, будет создавать объект, который не может изменять исходный экземпляр, в отличие от strChars as char[], который, если он действителен, предполагает, что теперь вы можете изменить исходный объект.

Tim MB 29.11.2019 14:20

@TimMB Существует неизменность коллекции (нельзя добавлять или удалять элементы) и неизменность элементов в коллекции. Я имел в виду последнее, в то время как вы, вероятно, их объединяете. Возврат массива гарантирует клиенту, что он не может добавлять / удалять элементы. Если это так, он перераспределяет массив и уверен, что это не повлияет на оригинал. При возврате списка таких гарантий не дается, и оригинал может быть поврежден (зависит от реализации). Изменение элементов в коллекции (будь то массив или список) может повлиять на оригинал, если тип элемента не является структурой.

Herman Schoenfeld 10.01.2020 10:58

Спасибо за разъяснение. Я все еще в замешательстве (вероятно, потому, что я из мира C++). Если str внутренне использует массив, а ToCharArray возвращает ссылку на этот массив, тогда клиент может изменить str, изменив элементы этого массива, даже если размер остается фиксированным. Тем не менее, вы пишете: «Ясно, что модификация strChars не изменит исходный объект str». Что мне здесь не хватает? Насколько я могу судить, в любом случае клиент может иметь доступ к внутреннему представлению, и, независимо от типа, это допускает какую-либо мутацию.

Tim MB 12.01.2020 17:06

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

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

Списки в .NET представляют собой оболочки над массивами и используют массив внутри. Временная сложность операций со списками такая же, как и с массивами, однако есть немного больше накладных расходов со всей добавленной функциональностью / простотой использования списков (например, автоматическое изменение размера и методы, которые поставляются с классом списка). В значительной степени я бы рекомендовал использовать списки во всех случаях, если нет веской причины для этого нет, например, если вам нужно написать чрезвычайно оптимизированный код или вы работаете с другим кодом, построенным на массивах.

Поскольку никто не упоминает: в C# массив - это список. MyClass[] и List<MyClass> реализуют IList<MyClass>. (например, void Foo(IList<int> foo) можно называть как Foo(new[] { 1, 2, 3 }) или Foo(new List<int> { 1, 2, 3 }))

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

Подробности:

«В C# массив - это список» Это неправда; массив не является List, он реализует только интерфейс IList.
Rufus L 10.11.2019 07:28

Они могут быть непопулярными, но я фанат массивов в игровых проектах. - Скорость итерации может быть важна в некоторых случаях, foreach в массиве имеет значительно меньше накладных расходов, если вы не делаете много для каждого элемента. - Добавление и удаление не так уж сложно с вспомогательными функциями - Он медленнее, но в тех случаях, когда вы строите его только один раз, это может не иметь значения - В большинстве случаев тратится меньше дополнительной памяти (действительно важно только для массивов структур) - Чуть меньше мусора, указателей и погони за указателями

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

Было бы неплохо, если бы List имел встроенный тип, чтобы можно было оптимизировать накладные расходы на оболочку и перечисление.

Заполнить список проще, чем массив. Для массивов необходимо знать точную длину данных, но для списков размер данных может быть любым. И вы можете преобразовать список в массив.

List<URLDTO> urls = new List<URLDTO>();

urls.Add(new URLDTO() {
    key = "wiki",
    url = "https://...",
});

urls.Add(new URLDTO()
{
    key = "url",
    url = "http://...",
});

urls.Add(new URLDTO()
{
    key = "dir",
    url = "https://...",
});

// convert a list into an array: URLDTO[]
return urls.ToArray();

Массивы против Списки - это классическая проблема ремонтопригодности и производительности. Практическое правило, которому следуют почти все разработчики, заключается в том, что вы должны стремиться к обоим, но когда они вступают в конфликт, выбирайте ремонтопригодность, а не производительность. Исключением из этого правила является ситуация, когда производительность уже оказалась проблемой. Если вы перенесете этот принцип в Arrays Vs. Списки, то вы получите следующее:

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

Другие вопросы по теме