C#がconstとして宣言できる型のセットを制限するのはなぜですか?
-
22-07-2019 - |
質問
コンパイラエラー CS0283 は、基本的なPODタイプ(および文字列、列挙、null参照)は、 const
として宣言できます。誰かがこの制限の理論的根拠について理論を持っていますか?たとえば、IntPtrなど、他の型のconst値を宣言できると便利です。
const
の概念は実際にはC#の構文シュガーであり、名前の使用をリテラル値に置き換えるだけだと考えています。たとえば、次の宣言がある場合、Fooへの参照はすべて" foo"に置き換えられます。コンパイル時に。
const string Foo = "foo";
これは変更可能なタイプを除外するため、コンパイル時に特定のタイプが変更可能かどうかを判断するのではなく、この制限を選択した可能性がありますか?
解決
C#仕様、10.4章-定数:
(C#3.0仕様では10.4、2.0のオンラインバージョンでは10.3)
定数は、定数値を表すクラスメンバーです。コンパイル時に計算できる値です。
これは基本的に、リテラルのみで構成される式のみを使用できることを示しています。コンパイラがコンパイル時にその実行を実行して結果を計算する方法がないため、メソッド、コンストラクタ(純粋なILリテラルとして表現できない)の呼び出しは使用できません。また、メソッドを不変としてタグ付けする方法がないため(つまり、入力と出力の間に1対1のマッピングがあります)、コンパイラがこれを行う唯一の方法は、ILを分析して入力パラメーター、特殊なケースの処理(IntPtrなど)、またはコードへのすべての呼び出しを禁止する場合以外に依存します。
例として、IntPtrは、値型ですが、まだ構造体であり、組み込みリテラルの1つではありません。そのため、IntPtrを使用する式では、IntPtr構造内のコードを呼び出す必要があります。これは、定数宣言には適さないものです。
私が考えることができる唯一の正当な定数値型の例は、宣言するだけでゼロで初期化されるもので、ほとんど役に立ちません。
コンパイラが定数を処理/使用する方法については、コード内の定数名の代わりに計算値を使用します。
したがって、次の効果があります。
- 元の定数名、宣言されたクラス、または名前空間への参照は、この場所のコードにコンパイルされません
- コードを逆コンパイルすると、元の" reference"定数には、前述のように存在せず、定数の値のみが存在します
- コンパイラはこれを使用して、不要なコードを最適化、または削除することさえできます。たとえば、
if(SomeClass.Version == 1)
は、SomeClass.Versionの値が1の場合、実際にifステートメントを削除し、実行中のコードブロックを保持します。定数の値が1でない場合、if文全体とそのブロックが削除されます。 - 定数への参照ではなく、定数の値がコードにコンパイルされるため、他のアセンブリの定数を使用しても、定数の値が変更された場合にコンパイルされたコードが自動的に更新されることはありませんない!)
つまり、次のシナリオの場合:
- アセンブリAには、値が1の「バージョン」という名前の定数が含まれています
- アセンブリBには、その定数からアセンブリAのバージョン番号を分析し、それを1と比較して、アセンブリで動作できることを確認する式が含まれています
- 誰かがアセンブリAを変更し、定数の値を2に増やし、Aを再構築します(Bは再構築しません)
この場合、コンパイルされた形式のアセンブリBは、1から1の値を比較します。これは、Bがコンパイルされたとき、定数の値が1だったためです。
実際、これがアセンブリBのアセンブリAの唯一の使用方法である場合、アセンブリBはアセンブリAに依存せずにコンパイルされます。アセンブリBでその式を含むコードを実行しても、アセンブリAは読み込まれません。
したがって、定数は決して変更されないものにのみ使用する必要があります。将来変更される可能性がある、または変更される値であり、他のすべてのアセンブリが同時に再構築されることを保証できない場合、読み取り専用フィールドは定数よりも適切です。これで問題ありません:
- public const Int32 NumberOfDaysInAWeekInGregorianCalendar = 7;
- public const Int32 NumberOfHoursInADayOnEarth = 24;
この間
他のヒント
この制限の理論的根拠に関する理論はありますか?
単なる理論として許可されている場合、私の理論では、プリミティブ型のconst値はMSILのリテラルオペコードパラメーターで表現できますが、他の非プリミティブ型の値はMSILではできないため、 tには、ユーザー定義型の値をリテラルとして表現する構文があります。
constの概念は実際にはC#の構文シュガーであり、名前の使用をリテラル値に置き換えるだけであると信じています
コンパイラは他の言語のconstオブジェクトで何をしますか?
実行時に評価される可変型には読み取り専用を使用できます。違いについては、この記事をご覧ください。
constsはC#の数字と文字列に制限されます。言い換えると、次のように記述します。
const string myName = "Bruce Wayne";
if (someVar == myName)
{
...
}
は実際には
として扱われますif (someVar == "Bruce Wayne")
{
...
}
そして、はい、C#コンパイラは文字列の等値演算子(==)を
として扱うのに十分スマートですstring1.Equals(string2)
定数として表現できるのは値型のみです(値とオブジェクト型の間にある文字列を除く)。
私にとっては問題ありません:オブジェクト(参照)はヒープに割り当てる必要がありますが、定数はまったく割り当てられません(コンパイル時に置き換えられるため)。
要するに、すべての単純型、列挙型、および文字列は不変ですが、たとえばStructは不変です。可変状態(フィールド、プロパティ、参照型への参照も含む)を持つStructを作成できます。そのため、コンパイラは(コンパイル時に)Struct変数の内部状態を変更できないことを確認できません。そのため、コンパイラーは、定数式で使用される型が定義上不変であることを確認する必要があります。