Ответ 1
.NET поддерживает поистине неизменяемые коллекции, представления только для чтения изменяемых коллекций и интерфейсы только для чтения, реализованные изменяемыми коллекциями.
Одна такая неизменяемая коллекция ImmutableArray<>
, которую вы можете создать как a.ToImmutableArray()
в вашем примере. Не забудьте взглянуть на другие варианты списков MSDN, потому что вам может быть лучше обслуживаться другая неизменяемая коллекция. Если вы хотите сделать копии исходной последовательности с небольшими изменениями, ImmutableList<>
может быть быстрее, например (массив дешевле для создания и доступ, хотя). Обратите внимание, что a.Add(...);
действителен, но возвращает новую коллекцию, а не меняет a
. Если у вас есть resharper, это предупредит вас, если вы проигнорируете возвращаемое значение чистого метода, такого как Add
(и может быть расширение roslyn, чтобы сделать что-то подобное, о котором я не знаю). Если вы идете по этому маршруту, попробуйте полностью пропустить List<>
и перейдем к неизменяемым коллекциям.
Представления только для чтения изменяемых коллекций немного менее безопасны, но поддерживаются в более старых версиях .NET. Тип упаковки называется ReadOnlyCollection<>
, который в вашем примере можно построить как a.AsReadOnly()
. Эта коллекция не гарантирует неизменность; это только защитники, которые вы не можете изменить. Некоторый другой бит кода, который имеет ссылку на базовый List<>
, все еще может изменить его. Кроме того, ReadOnlyCollection также накладывает дополнительные накладные расходы; поэтому вы не можете выиграть много, избегая неизменных коллекций по причинам производительности (TODO: сравнивайте эту претензию). Вы можете использовать обертку только для чтения, такую как это даже в публичном API, безопасно - нет (неразрешительного) способа получения основного списка. Однако, поскольку он часто не быстрее, чем неизменяемые коллекции, и он также не совсем безопасен, я рекомендую избегать ReadOnlyCollection<>
- я никогда больше не использую это лично.
Интерфейсы только для чтения, реализованные изменчивыми коллекциями, еще ниже по шкале безопасности, но быстро. Вы можете просто нарисовать List<>
как IReadOnlyList<>
, который вы можете сделать в своем примере как IReadOnlyList<int> lst = a
. Это мои предпочтения для внутреннего кода - вы по-прежнему получаете статическую безопасность, вы просто не защищены от вредоносного кода или кода, который использует проверки типов и отличает неразумно (но этого можно избежать с помощью обзоров кода в моем опыте). Я никогда не был укушен этим выбором, но он менее безопасен, чем два вышеупомянутых варианта. С другой стороны, он не берет никаких ассигнований и быстрее. Если вы это обычно делаете, вам может понадобиться определить метод расширения для выполнения upcast для вас (приведение может быть небезопасным в С#, потому что они не только безопасны для повышения, но, возможно, неудачные нисходящие потоки и пользовательские преобразования - так что это хорошо идея избегать явных приемов, где бы вы ни были).
Обратите внимание, что во всех случаях только сама последовательность доступна только для чтения. Не затрагиваются базовые объекты (например, int
или string
являются неизменяемыми, но более сложные объекты могут быть или не быть).
TL; DR:
- Для безопасности: используйте
a.ToImmutableArray()
, чтобы создать неизменяемую копию вImmutableArray<int>
. - Для производительности: используйте
IReadOnlyList<int>
, чтобы предотвратить случайную мутацию во внутреннем коде с минимальными издержками производительности. Имейте в виду, что кто-то может вернуть его обратно вList<>
(не делайте этого), делая это менее "безопасным" для публичного api. - Избегайте
a.AsReadOnly()
, который создаетReadOnlyCollection<int>
, если вы не работаете с устаревшей базой кода, которая не поддерживает более новые альтернативы, или если вы действительно знаете, что делаете и имеете особые потребности (например, действительно хотите изменить список в другом месте и иметь доступ только для чтения).