Причина в том, что это constможет быть применено только к полю, значение которого известно во время компиляции. Показанный инициализатор массива не является константным выражением в C #, поэтому он вызывает ошибку компилятора.
Объявление его readonlyрешает эту проблему, потому что значение не инициализируется до времени выполнения (хотя оно гарантированно инициализируется до первого использования массива).
В зависимости от того, чего вы в конечном итоге хотите достичь, вы можете также рассмотреть объявление enum:
Обратите внимание, что массив здесь не только для чтения, конечно; Названия [2] = "Уэльсские"; будет отлично работать во время выполнения
Марк Гравелл
46
Вы, вероятно, хотите, чтобы это тоже было статичным
tymtam
4
как насчет объявления массива "const" в теле метода, а не в классе?
Серхио
19
Извините за отрицательный голос, но const также подразумевает статичность. Объявление массива как доступного только для чтения не близко к обходному пути. это должно быть readonly staticкакое-то сходство с запрашиваемой семантикой.
Антон
3
@Антон, ты и твои "последователи" убрали downvote? Мне staticне требуется, чтобы это работало, просто добавьте возможность ссылаться Titlesбез экземпляра, но удалите возможность изменять значение для разных экземпляров (например, вы можете иметь конструктор с параметром, в зависимости от того, какое значение этого readonlyполя вы меняете ).
Синатр
57
Вы можете объявить массив как readonly, но имейте в виду, что вы можете изменить элемент readonlyмассива.
В .NET 4.5 и выше вы можете объявить список как IReadOnlyList <string> вместо IList <string>.
Гжегож Смулько
Просто чтобы прояснить, вы все равно можете изменить значения в IReadOnlyList (только не добавлять и не удалять элементы). Но да, объявление его как IReadOnlyList было бы лучше, чем IList.
KevinVictor
Вопрос о constНЕ readonly...
Юша Алеауб
51
Вы не можете создать массив const, потому что массивы являются объектами и могут быть созданы только во время выполнения, а объекты const разрешаются во время компиляции.
Вместо этого вы можете объявить массив как «только для чтения». Это имеет тот же эффект, что и const, за исключением того, что значение может быть установлено во время выполнения. Он может быть установлен только один раз, и после этого он будет иметь значение только для чтения (т.е. постоянное).
В этом посте подчеркивается, почему массивы не могут быть объявлены как константы
Radderz
1
Можно объявить константный массив; проблема заключается в его инициализации с постоянным значением. Единственный работающий пример, который приходит на ум - const int[] a = null;это не очень полезный, но действительно экземпляр константы массива.
Это создаст статическое свойство только для чтения, но все равно позволит вам изменять содержимое возвращаемого массива, но при повторном вызове свойства вы снова получите исходный неизмененный массив.
Для пояснения этот код аналогичен (или фактически сокращению):
Обратите внимание, что у этого подхода есть обратная сторона: новый массив фактически создается для каждой ссылки, поэтому, если вы используете очень большой массив, это может быть не самым эффективным решением. Но если вы повторно используете тот же массив (например, поместив его в закрытый атрибут), это снова откроет возможность изменить содержимое массива.
Если вы хотите иметь неизменяемый массив (или список), вы также можете использовать:
Но это по-прежнему сопряжено с риском для изменений, поскольку вы все равно можете привести его обратно к строке [] и изменить содержимое следующим образом:
Какая польза от использования свойства вместо поля в этом случае?
nicolay.anykienko
2
Поле не может возвращать новый объект при каждом вызове. Свойство - это своего рода «скрытая функция».
Мжепсон
1
Если вы ссылались на последний вариант, это можно сделать с помощью как поля, так и свойства, но, поскольку оно общедоступно, я предпочитаю свойство. Я никогда не использую открытое поле с момента появления свойств.
Мжепсон
1
Есть еще один недостаток первого подхода: вы не получите ошибку компиляции, если вы назначите Titles[0], например, - по сути, попытка назначения спокойно игнорируется. В сочетании с неэффективностью повторного создания массива, мне интересно, стоит ли вообще показывать этот подход. Напротив, 2-й подход эффективен, и вы должны сделать все возможное, чтобы победить неизменность.
mklement0
6
Если вы объявляете массив за интерфейсом IReadOnlyList, вы получаете постоянный массив с постоянными значениями, который объявляется во время выполнения:
Решение .NET Framework v4.5 + , улучшающее ответ tdbeckett :
using System.Collections.ObjectModel;// ...publicReadOnlyCollection<string>Titles{get;}=newReadOnlyCollection<string>(newstring[]{"German","Spanish","Corrects","Wrongs"});
Примечание. Учитывая, что коллекция концептуально постоянна, может иметь смысл staticобъявить ее на уровне класса .
Вышеупомянутое:
Инициализирует неявное поле поддержки свойства один раз с массивом.
Обратите внимание, что { get; }- то есть объявление только метода получения свойства - это то, что делает само свойство неявно доступным только для чтения (попытка объединиться readonlyс { get; }ним на самом деле является синтаксической ошибкой).
В качестве альтернативы вы можете просто опустить { get; }и добавить, readonlyчтобы создать поле вместо свойства, как в вопросе, но использование открытых элементов данных в качестве свойств, а не полей - хорошая привычка для формирования.
Создает array- как структуры ( с учетом индексированного доступа ) , что по- настоящему и решительно только для чтения (концептуально постоянная, после создания), как в отношении:
предотвращение изменения коллекции в целом (например, путем удаления или добавления элементов или назначения новой коллекции переменной).
предотвращение модификации отдельных элементов .
(Даже непрямая модификация не возможно - в отличии с IReadOnlyList<T>раствором, в котором литой может быть использован , чтобы получить доступ на запись к элементам , как показан на полезном ответ mjepsen игровых .
То же уязвимость относится к(string[]) IReadOnlyCollection<T> интерфейсу , который, несмотря на сходство в названии к классуReadOnlyCollection , даже не поддерживает индексированный доступ , что делает его принципиально непригодным для предоставления доступа, подобного массиву.)
@mortb: К сожалению, IReadOnlyCollectionне поддерживает индексированный доступ, поэтому его нельзя использовать здесь. Кроме того, вроде IReadOnlyList(который имеет индексированный доступ) он подвержен манипуляциям с элементами при приведении к string[]. Другими словами: ReadOnlyCollection(к которому вы не можете привести массив строк) - это самое надежное решение. Не использовать геттер - это вариант (и я обновил ответ, чтобы отметить это), но с общедоступными данными, вероятно, лучше придерживаться свойства.
mklement0
5
Для моих нужд я определяю staticмассив, а не невозможный, constи он работает:
public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Простое удаление constиз примера OP также работает, но это (или ваш ответ) позволяет изменить и Titlesэкземпляр, и любое значение. Так какой смысл в этом ответе?
Синатр
@ Синатр, я ответил на это 3 года назад, когда начал работать в C #. Я оставил это, теперь я в мире Java. Возможно, я забыл добавитьreadonly
ALZ
После второй мысли, ваш ответ является прямым , как сделать OP код работать без каких - либо const/ readonlyсоображений, просто делает его работу (например , если constбыл синтаксис ошибка). Для некоторых людей это кажется ценным ответом (возможно, они также пытались использовать constпо ошибке?).
Синатр
5
Вы можете использовать другой подход: определить постоянную строку для представления вашего массива, а затем разбить строку на массив, когда вам это нужно, например,
Я думаю, что стоимость выполнения разделения намного превосходит любые выгоды, получаемые при определении const. Но +1 за уникальный подход и нестандартное мышление! ;)
Раддерз
Я собирался опубликовать то же решение, а затем увидел, что вопреки резким и отрицательным замечаниям, это было действительно идеально для моего сценария, где мне нужно было передать const в атрибут, а затем я разделил значение в конструкторе атрибута, получить то, что мне нужно. и я не вижу причин, почему это будет иметь производительность, поскольку атрибуты создаются не для каждого экземпляра.
Калпеш Попат
4
Для полноты картины теперь у нас есть ImmutableArrays. Это должно быть действительно неизменным:
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;publicReadOnlyCollection<string>Titles{get{returnnewList<string>{"German","Spanish","Corrects","Wrongs"}.AsReadOnly();}}
Это очень похоже на создание массива только для чтения.
Вы можете просто сделать это как public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; нет необходимости пересоздавать список при каждом извлечении, если вы все равно сделаете его ReadOnlyCollection.
Нергудс
3
Это единственный правильный ответ. Вы не можете сделать это в настоящее время.
Все остальные ответы предлагают использовать статические переменные только для чтения, которые похожи , но не совпадают с константами. Константа жестко запрограммирована в сборке. Статическая переменная только для чтения устанавливается один раз, вероятно, когда объект инициализируется.
Массивы, вероятно, являются одной из тех вещей, которые могут быть оценены только во время выполнения. Константы должны быть оценены во время компиляции. Попробуйте использовать «только для чтения» вместо «const».
В качестве альтернативы, чтобы обойти проблему «элементы могут быть изменены» с массивом только для чтения, вы можете использовать вместо этого статическое свойство. (Отдельные элементы все еще могут быть изменены, но эти изменения будут сделаны только в локальной копии массива.)
Ответы:
Да, но вы должны объявить это
readonly
вместоconst
:Причина в том, что это
const
может быть применено только к полю, значение которого известно во время компиляции. Показанный инициализатор массива не является константным выражением в C #, поэтому он вызывает ошибку компилятора.Объявление его
readonly
решает эту проблему, потому что значение не инициализируется до времени выполнения (хотя оно гарантированно инициализируется до первого использования массива).В зависимости от того, чего вы в конечном итоге хотите достичь, вы можете также рассмотреть объявление enum:
источник
readonly static
какое-то сходство с запрашиваемой семантикой.static
не требуется, чтобы это работало, просто добавьте возможность ссылатьсяTitles
без экземпляра, но удалите возможность изменять значение для разных экземпляров (например, вы можете иметь конструктор с параметром, в зависимости от того, какое значение этогоreadonly
поля вы меняете ).Вы можете объявить массив как
readonly
, но имейте в виду, что вы можете изменить элементreadonly
массива.Подумайте об использовании enum, как предложил Коди, или IList.
источник
const
НЕreadonly
...Вы не можете создать массив const, потому что массивы являются объектами и могут быть созданы только во время выполнения, а объекты const разрешаются во время компиляции.
Вместо этого вы можете объявить массив как «только для чтения». Это имеет тот же эффект, что и const, за исключением того, что значение может быть установлено во время выполнения. Он может быть установлен только один раз, и после этого он будет иметь значение только для чтения (т.е. постоянное).
источник
const int[] a = null;
это не очень полезный, но действительно экземпляр константы массива.Начиная с C # 6 вы можете написать это так:
Смотрите также: C #: новый и улучшенный C # 6.0 (в частности, глава «Функции и свойства, связанные с выражением»)
Это создаст статическое свойство только для чтения, но все равно позволит вам изменять содержимое возвращаемого массива, но при повторном вызове свойства вы снова получите исходный неизмененный массив.
Для пояснения этот код аналогичен (или фактически сокращению):
Обратите внимание, что у этого подхода есть обратная сторона: новый массив фактически создается для каждой ссылки, поэтому, если вы используете очень большой массив, это может быть не самым эффективным решением. Но если вы повторно используете тот же массив (например, поместив его в закрытый атрибут), это снова откроет возможность изменить содержимое массива.
Если вы хотите иметь неизменяемый массив (или список), вы также можете использовать:
Но это по-прежнему сопряжено с риском для изменений, поскольку вы все равно можете привести его обратно к строке [] и изменить содержимое следующим образом:
источник
Titles[0]
, например, - по сути, попытка назначения спокойно игнорируется. В сочетании с неэффективностью повторного создания массива, мне интересно, стоит ли вообще показывать этот подход. Напротив, 2-й подход эффективен, и вы должны сделать все возможное, чтобы победить неизменность.Если вы объявляете массив за интерфейсом IReadOnlyList, вы получаете постоянный массив с постоянными значениями, который объявляется во время выполнения:
Доступно в .NET 4.5 и выше.
источник
Решение .NET Framework v4.5 + , улучшающее ответ tdbeckett :
Примечание. Учитывая, что коллекция концептуально постоянна, может иметь смысл
static
объявить ее на уровне класса .Вышеупомянутое:
Инициализирует неявное поле поддержки свойства один раз с массивом.
Обратите внимание, что
{ get; }
- то есть объявление только метода получения свойства - это то, что делает само свойство неявно доступным только для чтения (попытка объединитьсяreadonly
с{ get; }
ним на самом деле является синтаксической ошибкой).В качестве альтернативы вы можете просто опустить
{ get; }
и добавить,readonly
чтобы создать поле вместо свойства, как в вопросе, но использование открытых элементов данных в качестве свойств, а не полей - хорошая привычка для формирования.Создает array- как структуры ( с учетом индексированного доступа ) , что по- настоящему и решительно только для чтения (концептуально постоянная, после создания), как в отношении:
(Даже непрямая модификация не возможно - в отличии с
IReadOnlyList<T>
раствором, в котором литой может быть использован , чтобы получить доступ на запись к элементам , как показан на полезном ответ mjepsen игровых . То же уязвимость относится к(string[])
IReadOnlyCollection<T>
интерфейсу , который, несмотря на сходство в названии к классуReadOnlyCollection
, даже не поддерживает индексированный доступ , что делает его принципиально непригодным для предоставления доступа, подобного массиву.)источник
IReadOnlyCollection
не поддерживает индексированный доступ, поэтому его нельзя использовать здесь. Кроме того, вродеIReadOnlyList
(который имеет индексированный доступ) он подвержен манипуляциям с элементами при приведении кstring[]
. Другими словами:ReadOnlyCollection
(к которому вы не можете привести массив строк) - это самое надежное решение. Не использовать геттер - это вариант (и я обновил ответ, чтобы отметить это), но с общедоступными данными, вероятно, лучше придерживаться свойства.Для моих нужд я определяю
static
массив, а не невозможный,const
и он работает:public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
источник
const
из примера OP также работает, но это (или ваш ответ) позволяет изменить иTitles
экземпляр, и любое значение. Так какой смысл в этом ответе?readonly
const
/readonly
соображений, просто делает его работу (например , еслиconst
был синтаксис ошибка). Для некоторых людей это кажется ценным ответом (возможно, они также пытались использоватьconst
по ошибке?).Вы можете использовать другой подход: определить постоянную строку для представления вашего массива, а затем разбить строку на массив, когда вам это нужно, например,
Этот подход дает вам константу, которая может быть сохранена в конфигурации и преобразована в массив при необходимости.
источник
Для полноты картины теперь у нас есть ImmutableArrays. Это должно быть действительно неизменным:
Требуется ссылка System.Collections.Immutable NuGet
https://msdn.microsoft.com/en-us/library/mt452182(v=vs.111).aspx
источник
Это способ сделать то, что вы хотите:
Это очень похоже на создание массива только для чтения.
источник
public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();
; нет необходимости пересоздавать список при каждом извлечении, если вы все равно сделаете его ReadOnlyCollection.Это единственный правильный ответ. Вы не можете сделать это в настоящее время.
Все остальные ответы предлагают использовать статические переменные только для чтения, которые похожи , но не совпадают с константами. Константа жестко запрограммирована в сборке. Статическая переменная только для чтения устанавливается один раз, вероятно, когда объект инициализируется.
Они иногда взаимозаменяемы, но не всегда.
источник
Я верю, что вы можете сделать это только для чтения.
источник
Массивы, вероятно, являются одной из тех вещей, которые могут быть оценены только во время выполнения. Константы должны быть оценены во время компиляции. Попробуйте использовать «только для чтения» вместо «const».
источник
В качестве альтернативы, чтобы обойти проблему «элементы могут быть изменены» с массивом только для чтения, вы можете использовать вместо этого статическое свойство. (Отдельные элементы все еще могут быть изменены, но эти изменения будут сделаны только в локальной копии массива.)
Конечно, это не будет особенно эффективно, поскольку каждый раз создается новый массив строк.
источник
Лучшая альтернатива:
источник