const配列を宣言する


458

次のようなものを書くことは可能ですか?

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

staticを使用できます、public static string [] Titles = new string [] {"German"、 "Spanish"};
レイ

回答:


691

はい。ただし、次のreadonly代わりに宣言する必要がありますconst

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

その理由は、constコンパイル時に値がわかっているフィールドにのみ適用できるためです。表示した配列初期化子はC#の定数式ではないため、コンパイラエラーが発生します。

readonly値は実行時まで初期化されないため、宣言するとこの問題が解決されます(ただし、配列が初めて使用される前に初期化されていることが保証されています)。

最終的に達成したいものに応じて、列挙型の宣言を検討することもできます。

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

115
もちろん、ここの配列は読み取り専用ではないことに注意してください。Titles [2] = "ウェールズ語"; 実行時に問題なく動作します
Marc Gravell

46
あなたはおそらくそれも静的にしたいでしょう
ティムタム2013年

4
クラス本体ではなく、メソッド本体で「const」配列を宣言するのはどうですか?
serhio 2014年

19
反対票を投じて申し訳ありませんが、constは静的も意味します。配列を読み取り専用として宣言することは、回避策に近づきません。readonly static要求されたセマンティクスに似ている必要があります。
アントン

3
@Anton、あなたとあなたの「フォロワー」は反対投票を削除しましたか?私にstaticはそれを機能させる必要はありません、Titlesインスタンスなしで参照する可能性を追加するだけで、異なるインスタンスの値を変更する可能性を削除します(たとえば、readonlyフィールドの値を変更するパラメーターに応じてコンストラクターを持つことができます)。
Sinatr 2016年

57

配列はとして宣言できreadonlyますが、readonly配列の要素は変更できることに注意してください。

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

Codyが示唆するように、列挙型またはIListの使用を検討してください。

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

31
.NET 4.5以降では、リストをIList <string>ではなくIReadOnlyList <string>として宣言できます。
Grzegorz Smulko 2014

明確にするために、IReadOnlyListの値は変更できます(要素を追加または削除しないでください)。しかし、はい、それをIReadOnlyListとして宣言する方がIListよりも優れています。
KevinVictor

問題はconstNOT についてreadonlyです...
Yousha Aleayoub

51

配列はオブジェクトであり、実行時にのみ作成でき、constエンティティはコンパイル時に解決されるため、「const」配列を作成することはできません。

代わりにできることは、配列を「読み取り専用」として宣言することです。これは、値を実行時に設定できることを除いて、constと同じ効果があります。これは一度だけ設定でき、その後は読み取り専用(つまりconst)の値になります。


2
この投稿では、配列を定数として宣言できない理由を強調しています
Radderz

1
定数配列を宣言することは可能です。問題は、定数値で初期化することです。頭に浮かぶ唯一の実用的な例const int[] a = null;は、あまり役に立ちませんが、実際には配列定数のインスタンスです。
ウォルドルムプス

これが唯一の正解です。
Yousha Aleayoub

17

C#6以降、次のように記述できます。

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

参照:C#:新しく改善されたC#6.0(具体的には、「式本体関数とプロパティ」の章)

これにより、読み取り専用の静的プロパティが作成されますが、返される配列の内容を変更できますが、プロパティを再度呼び出すと、元の変更されていない配列が再び取得されます。

明確にするために、このコードは以下と同じです(または実際の省略形)。

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 []にキャストバックして内容を変更できるため、変更のリスクがあります。

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

1
この場合、フィールドの代わりにプロパティを使用する利点は何ですか?
nicolay.anykienko 2016年

2
フィールドは、呼び出しごとに新しいオブジェクトを返すことはできません。プロパティは基本的に一種の「変装した関数」です。
mjepson 2016

1
最後のオプションを参照している場合、それはフィールドまたはプロパティの両方を使用して行うことができますが、パブリックであるため、プロパティを使用します。プロパティの導入以来、私はパブリックフィールドを使用していません。
mjepson 16

1
最初のアプローチには別の欠点がありTitles[0]ます。たとえば、に割り当ててもコンパイルエラーが発生しません-実際には、割り当ての試行は静かに無視されます。毎回アレイを再作成する非効率性と組み合わせると、このアプローチは示す価値があるのでしょうか。対照的に、2番目のアプローチは効率的であり、不変性を打ち負かすために自分の道を離れなければなりません。
mklement0 2017

6

IReadOnlyListインターフェイスの背後で配列を宣言すると、実行時に宣言された定数値を持つ定数配列が得られます。

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

.NET 4.5以降で使用できます。


6

A +ソリューションの.NET FrameworkのV4.5に向上tdbeckettの答え

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>ソリューション、キャストが要素にゲインの書き込みアクセスに使用することができますに示すように、mjepsenの便利な答え。 同じ脆弱性が適用されるインタフェース名の類似性にもかかわらず、これは、クラスでもサポートされていないインデックス付きのアクセスを配列のようなアクセスを提供するための、それは根本的に適しません、。)(string[])
      IReadOnlyCollection<T> ReadOnlyCollection

1
@mortb:残念ながら、IReadOnlyCollectionインデックス付きアクセスをサポートしていないため、ここでは使用できません。さらに、IReadOnlyList(インデックス付きアクセスを持っている)のように、にキャストバックすることによる要素操作の影響を受けやすくなっていますstring[]。つまり、ReadOnlyCollection(文字列配列をキャストできない)が最も堅牢なソリューションです。ゲッターを使用しないことはオプションです(そして、私はそれを注記するために答えを更新しました)が、パブリックデータでは、おそらくプロパティに固執する方が良いでしょう。
mklement0 2017

5

私のニーズのためstaticに、不可能ではなく配列を定義しconst、それが機能します: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };


1
constOPの例から単純に削除することもできますが、それ(またはあなたの答え)により、Titlesインスタンスと任意の値の両方を変更できます。それでは、この回答のポイントは何ですか?
Sinatr 2016年

@Sinatr、私は3年前にC#での作業に取り組みました。私はそれを去りました、今はJavaの世界にいます。追加するのを忘れたのかもしれませんreadonly
ALZ

考え直した後の答えは、/を考慮せずにOPコードを機能させる直接の方法です。これは、(構文の誤りのように)単に機能させるだけです。一部の人にとっては、それは貴重な答えであるように思われます(おそらく、彼らは誤って使用しようとしたのでしょうか?)。constreadonlyconstconst
Sinatr 2016年

5

別のアプローチを取ることもできます。たとえば、配列を表す定数文字列を定義し、必要に応じて文字列を配列に分割します。

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

このアプローチでは、構成に格納して必要に応じて配列に変換できる定数が提供されます。


8
分割を実行するコストは、constを定義することで得られるメリットをはるかに上回っていると思います。しかし、ユニークなアプローチと箱から出して考えるための+1!;)
Radderz、2015年

私は同じ解決策を投稿しようとしていて、厳しいと否定的な発言に反してこれを見ました、これは実際に私のシナリオに最適でした、私は属性にconstを渡す必要があり、次に属性コンストラクターで値を分割しました必要なものを手に入れます。また、インスタンスごとに属性が作成されないため、パフォーマンスコストがかかる理由はわかりません。
Kalpesh Popat 2018年


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<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; とにかくそれをReadOnlyCollectionにすれば、毎回の検索でリストを再作成する必要はありません。
Nyerguds

3

これが唯一の正解です。現在、これを行うことはできません。

他のすべての答えは、に似ていますが定数とは異なる静的な読み取り専用変数を使用することを提案しています。定数はアセンブリにハードコードされています。静的な読み取り専用変数は、おそらくオブジェクトが初期化されるときに一度設定できます。

これらは時々交換可能ですが、常にではありません。


2

読み取り専用にすることしかできないと思います。


2

配列はおそらく、実行時にのみ評価できるものの1つです。定数はコンパイル時に評価する必要があります。「const」の代わりに「readonly」を使用してみてください。


0

別の方法として、読み取り専用の配列で変更可能な要素の問題を回避するには、代わりに静的プロパティを使用できます。(個々の要素は変更できますが、これらの変更は配列のローカルコピーに対してのみ行われます。)

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

もちろん、毎回新しい文字列配列が作成されるため、これは特に効率的ではありません。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.