2011-02-28 2 views
284

Можно ли написать что-то похожее на следующее?Объявить массив const

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" }; 
+0

статические могут быть использованы, public static string [] Titles = new string [] {"German", "Spanish"}; –

ответ

468

Да, но вы должны объявить его readonly вместо const:

public static readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" }; 

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

Объявление readonly решает эту проблему, потому что значение не инициализируется до времени выполнения (хотя гарантировано, что оно будет инициализировано до первого использования массива).

В зависимости от того, что это такое, что вы в конечном итоге хотите достичь, вы можете также рассмотреть вопрос об объявлении перечисление:

public enum Titles { German, Spanish, Corrects, Wrongs }; 
+85

Обратите внимание, что * массив * здесь не является readonly, конечно; Названия [2] = "Вельш"; будет работать нормально во время выполнения –

+38

Возможно, вы тоже статично статичны – mayu

+3

, как насчет объявления массива «const» в теле метода, а не в классе? – serhio

2

Я считаю, что вы можете сделать это только для чтения.

1

Массивы, вероятно, являются одной из тех вещей, которые могут быть оценены только при времени выполнения. Константы должны оцениваться во время компиляции. Попробуйте использовать «readonly» вместо «const».

38

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

Вместо этого вы должны объявить свой массив как «readonly». У этого есть тот же эффект, что и const, за исключением того, что значение может быть установлено во время выполнения. Это может быть только , установленное один раз, и после этого оно имеет значение readonly (т.е. const).

48

Вы можете объявить массив как readonly, но имейте в виду, что вы можете изменить элемент массива readonly.

public readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" }; 
... 
Titles[0] = "bla"; 

Рассмотрите возможность использования перечисления, как предложил Коди, или IList.

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly(); 
+18

В .NET 4.5 и выше вы можете объявить список как IReadOnlyList вместо IList . –

6

Для моих потребностей я определяю static массив, вместо того, чтобы невозможно const и она работает: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

+1

Простое удаление 'const' из примера OP также работает, но это (или ваш ответ) позволяет изменить оба: экземпляр' Titles' и любое значение. Так в чем смысл этого ответа? – Sinatr

+0

@ Синат, я ответил на это 3 года назад, когда я перешел работать на C#. Я оставил его, теперь я в мире Java. Возможно, я забыл добавить 'readonly' – ALZ

+0

. После второй мысли ваш ответ является прямым * как заставить OP-код работать * без каких-либо' const'/'readonly' соображений, просто * делая его работу * (например, если' const' была синтаксической ошибкой). Для некоторых людей это кажется * ценным * ответом (возможно, они также пытались использовать ['const'] (https://msdn.microsoft.com/en-us/library/e6w8fe1b.aspx) по ошибке?). – Sinatr

7

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

const string DefaultDistances = "5,10,15,20,25,30,40,50"; 
public static readonly string[] distances = DefaultDistances.Split(','); 

Этот подход дает вам константу, которая может быть сохранена в конфигурации и преобразована в массив при необходимости.

Аластер

+7

Это бесполезно. Он не решает проблему «элементы в массиве не только для чтения». – Nyerguds

+3

Я думаю, что стоимость выполнения раскола намного опережает любые выгоды от определения const. Но +1 для уникального подхода и мышления вне коробки! ;) – Radderz

3

Это способ сделать то, что вы хотите:

using System; 
using System.Collections.ObjectModel; 
using System.Collections.Generic; 

public ReadOnlyCollection<string> Titles { get { return new List<string> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();}} 

Он очень похож делать только для чтения массива.

+8

Вы можете просто сделать это как «public static readonly ReadOnlyCollection Titles = new List {« Немецкий »,« Испанский »,« Исправить »,« Неправильно »} .AsReadOnly();'; нет необходимости воссоздавать список при каждом извлечении, если вы все равно сделаете его ReadOnlyCollection. – Nyerguds

1

В качестве альтернативы, чтобы обойти проблему, связанную с элементами, с помощью массива readonly, вместо этого вы можете использовать статическое свойство. (Отдельные элементы еще можно изменить, но эти изменения будут сделаны только на локальной копии массива.)

public static string[] Titles 
{ 
    get 
    { 
     return new string[] { "German", "Spanish", "Corrects", "Wrongs"}; 
    } 
} 

Конечно, это не будет особенно эффективным, как новый массив строк создается каждый раз, ,

6

Поскольку C# 6 вы можете написать это нравится:

public static string[] Titles => new string[] { "German", "Spanish", "Corrects", "Wrongs" }; 

Смотрите также: C# : The New and Improved C# 6.0 (в частности, главы "Expression работоспособной Функции и свойство")

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

Для уточнения, этот код так же, как (или на самом деле сокращенная запись):

public static string[] Titles 
{ 
    get { return new string[] { "German", "Spanish", "Corrects", "Wrongs" }; } 
} 

Пожалуйста, обратите внимание, что есть недостаток этого подхода: Новый массив фактически экземпляр на каждой ссылку , поэтому, если вы используете очень большой массив, это может быть не самое эффективное решение. Но если вы повторно используете один и тот же массив (например, поместив его в частный атрибут), он снова откроет возможность изменить содержимое массива.

Если вы хотите иметь неизменный массив (или список), вы также можете использовать:

public static IReadOnlyList<string> Titles { get; } = new string[] { "German", "Spanish", "Corrects", "Wrongs" }; 

Но это по-прежнему есть риск изменения, как вы все еще можете бросить его обратно в строку [] и изменять содержимое, например, как:

((string[]) Titles)[1] = "French"; 
+1

Какова прибыль использования объекта вместо поля в этом случае? –

+2

Поле не может вернуть новый объект для каждого вызова. Свойство - это своего рода «скрытая функция». – mjepson

+1

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

3

.NET Framework v4.5 + решения, что улучшает tdbeckett's answer:

using System.Collections.ObjectModel; 

// ... 

public ReadOnlyCollection<string> Titles { get; } = new ReadOnlyCollection<string>(
    new string[] { "German", "Spanish", "Corrects", "Wrongs" } 
); 

Примечание: учитывая, что коллекция концептуально постоянна, имеет смысл сделать это static, чтобы объявить ее на уровне класса.

выше:

  • Инициализирует неявное поле подложки отеля раз с массивом.

    • Обратите внимание, что { get; } - то есть, объявляя только свойство поглотитель - это то, что делает сам неявно свойство только для чтения (пытаясь объединить readonly с { get; } на самом деле ошибка синтаксиса).

    • В качестве альтернативы, вы можете просто опустить { get; } и добавить readonly создать поле вместо свойства, как и в вопросе, но подвергая общественные элементы данных как свойства, а не полей является хорошей привычкой, чтобы сформировать ,

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

    • , предотвращающий модификацию коллекции (например, путем удаления или добавления элементов или путем замены коллекции в целом)
    • предотвращение модификации отдельных элементов.
      (Даже косвенным модификация не возможно - в отличие с IReadOnlyList<T> раствора, где (string[]) бросок может быть использован, чтобы получить доступ на запись к элементам, как показано на mjepsen's helpful answer
      То же самое относится и к уязвимости интерфейса IReadOnlyCollection<T>,. который, несмотря на свое название, сходство с классReadOnlyCollection, даже не поддерживает с индексом, что делает его принципиально непригодным для обеспечения доступа к массиву).
+1

@mortb: К сожалению, 'IReadOnlyCollection' не поддерживает индексированный доступ, поэтому его здесь нельзя использовать. Кроме того, как 'IReadOnlyList' (который имеет индексированный доступ), он подвержен обработке элементов, возвращаясь к' string [] '. Другими словами: 'ReadOnlyCollection' (который вы не можете отличить строковый массив) является наиболее надежным решением. Не использовать геттер - это вариант (и я обновил ответ, чтобы отметить это), но с данными _public_, вероятно, лучше придерживаться свойства. – mklement0

+0

@mortb: P.S .: Я также обновил ответ, чтобы показать, почему 'IReadOnlyCollection' не является вариантом. – mklement0

1

Если вы объявляете массив за интерфейс IReadOnlyList вы получаете постоянный массив с постоянными значениями, объявленный во время выполнения:

public readonly IReadOnlyList<string> Titles = new [] {"German", "Spanish", "Corrects", "Wrongs" }; 

Доступен в .NET 4.5 и выше.

Смежные вопросы