X 3015:2008 (ISO/IEC 23270:2006)
(1)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
目 次
ページ
序文 ··································································································································· 1
1 適用範囲 ························································································································· 1
2 適合性···························································································································· 1
3 引用規格 ························································································································· 2
4 用語及び定義 ··················································································································· 3
5 表記法···························································································································· 6
6 頭字語及び略語 ················································································································ 7
7 規格概説 ························································································································· 7
8 言語概要 ························································································································· 8
8.1 プログラム例 ················································································································ 8
8.2 型 ······························································································································· 9
8.2.1 あらかじめ定義された型 ······························································································ 11
8.2.2 変換 ························································································································· 13
8.2.3 配列型 ······················································································································ 14
8.2.4 型システム統合 ·········································································································· 16
8.3 変数及び仮引数 ············································································································ 17
8.4 自動メモリ管理 ············································································································ 21
8.5 式 ······························································································································ 24
8.6 文 ······························································································································ 24
8.7 クラス ························································································································ 27
8.7.1 定数 ························································································································· 29
8.7.2 フィールド ················································································································ 30
8.7.3 メソッド ··················································································································· 31
8.7.4 特性 ························································································································· 33
8.7.5 イベント ··················································································································· 34
8.7.6 演算子 ······················································································································ 35
8.7.7 添字子 ······················································································································ 37
8.7.8 インスタンス構築子 ···································································································· 38
8.7.9 終了化子 ··················································································································· 39
8.7.10 静的構築子 ··············································································································· 39
8.7.11 継承 ························································································································ 40
8.7.12 静的クラス ··············································································································· 42
8.7.13 部分型宣言 ··············································································································· 42
8.8 構造体 ························································································································ 43
8.9 インタフェース ············································································································ 44
X 3015:2008 (ISO/IEC 23270:2006) 目次
(2)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
8.10 委譲 ·························································································································· 45
8.11 enum ························································································································· 46
8.12 名前空間及びアセンブリ ······························································································· 47
8.13 版管理 ······················································································································· 49
8.14 外部別名 ···················································································································· 52
8.15 属性 ·························································································································· 54
8.16 総称 ·························································································································· 55
8.16.1 なぜ総称か ··············································································································· 55
8.16.2 総称の生成及び消費 ··································································································· 56
8.16.3 複数の型仮引数 ········································································································· 57
8.16.4 制約 ························································································································ 57
8.16.5 総称メソッド ············································································································ 59
8.17 無名メソッド ·············································································································· 60
8.18 反復子 ······················································································································· 63
8.19 null許容型 ··············································································································· 67
9 字句構造 ························································································································ 70
9.1 プログラム ·················································································································· 70
9.2 文法 ··························································································································· 70
9.2.1 字句文法 ··················································································································· 70
9.2.2 構文文法 ··················································································································· 70
9.2.3 文法のあいまい性 ······································································································· 70
9.3 字句解析 ····················································································································· 71
9.3.1 行終端子 ··················································································································· 72
9.3.2 注釈 ························································································································· 72
9.3.3 空白類 ······················································································································ 74
9.4 字句 ··························································································································· 74
9.4.1 Unicode逆斜線表記 ····································································································· 75
9.4.2 識別子 ······················································································································ 75
9.4.3 キーワード ················································································································ 77
9.4.4 リテラル ··················································································································· 78
9.4.4.1 真理値リテラル ········································································································ 78
9.4.4.2 整数リテラル ··········································································································· 78
9.4.4.3 実数リテラル ··········································································································· 79
9.4.4.4 文字リテラル ··········································································································· 80
9.4.4.5 文字列リテラル ········································································································ 81
9.4.4.6 nullリテラル ········································································································· 83
9.4.5 演算子及び区切り子 ···································································································· 83
9.5 前処理指令 ·················································································································· 84
9.5.1 条件付きコンパイル用記号 ··························································································· 85
X 3015:2008 (ISO/IEC 23270:2006) 目次
(3)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
9.5.2 前処理式 ··················································································································· 86
9.5.3 宣言指令 ··················································································································· 86
9.5.4 条件付きコンパイル指令 ······························································································ 87
9.5.5 診断指令 ··················································································································· 90
9.5.6 領域制御 ··················································································································· 91
9.5.7 行指令 ······················································································································ 91
9.5.8 プラグマ指令 ············································································································· 92
10 基本概念 ······················································································································ 93
10.1 アプリケーション開始 ·································································································· 93
10.2 アプリケーション終了 ·································································································· 94
10.3 宣言 ·························································································································· 94
10.4 メンバ ······················································································································· 98
10.4.1 名前空間のメンバ ······································································································ 98
10.4.2 構造体のメンバ ········································································································· 98
10.4.3 列挙のメンバ ············································································································ 98
10.4.4 クラスのメンバ ········································································································· 98
10.4.5 インタフェースのメンバ ····························································································· 99
10.4.6 配列のメンバ ············································································································ 99
10.4.7 委譲のメンバ ············································································································ 99
10.5 メンバアクセス ··········································································································· 99
10.5.1 宣言されたアクセス可能性 ·························································································· 99
10.5.2 アクセス可能領域 ····································································································· 100
10.5.3 インスタンスメンバのための限定公開アクセス ······························································ 103
10.5.4 アクセス可能性制約 ·································································································· 104
10.6 呼出し情報及び多重定義 ······························································································ 105
10.7 有効範囲 ··················································································································· 106
10.7.1 名前の隠ぺい ··········································································································· 109
10.7.1.1 入れ子による隠ぺい ······························································································· 109
10.7.1.2 継承による隠ぺい ·································································································· 110
10.8 名前空間及び型名 ······································································································· 111
10.8.1 非限定名 ················································································································· 113
10.8.2 完全限定名 ·············································································································· 113
10.9 自動メモリ管理機能 ···································································································· 114
10.10 実行順序 ················································································································· 118
11 型 ······························································································································ 118
11.1 値型 ························································································································· 118
11.1.1 System.ValueType型 ···························································································· 120
11.1.2 省略時構築子 ··········································································································· 120
11.1.3 構造体型 ················································································································· 121
X 3015:2008 (ISO/IEC 23270:2006) 目次
(4)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
11.1.4 単純型 ···················································································································· 121
11.1.5 整数型 ···················································································································· 122
11.1.6 浮動小数点型 ··········································································································· 122
11.1.7 decimal(10進実数)型 ··························································································· 123
11.1.8 bool型 ·················································································································· 124
11.1.9 列挙型 ···················································································································· 124
11.2 参照型 ······················································································································ 124
11.2.1 クラス型 ················································································································· 125
11.2.2 object型 ·············································································································· 125
11.2.3 string(文字列)型 ································································································ 125
11.2.4 インタフェース型 ····································································································· 125
11.2.5 配列型 ···················································································································· 126
11.2.6 委譲型 ···················································································································· 126
11.2.7 空型 ······················································································································· 126
11.3 ボックス化及びボックス化解除 ····················································································· 126
11.3.1 ボックス化変換 ········································································································ 126
11.3.2 ボックス化解除変換 ·································································································· 128
11.4 null許容型 ·············································································································· 128
11.4.1 メンバ ···················································································································· 129
11.4.2 実装インタフェース ·································································································· 129
12 変数 ··························································································································· 129
12.1 変数の種類 ················································································································ 130
12.1.1 静的変数 ················································································································· 130
12.1.2 インスタンス変数 ····································································································· 130
12.1.2.1 クラスのインスタンス変数······················································································· 130
12.1.2.2 構造体のインスタンス変数······················································································· 130
12.1.3 配列要素 ················································································································· 130
12.1.4 値仮引数 ················································································································· 131
12.1.5 参照仮引数 ·············································································································· 131
12.1.6 出力仮引数 ·············································································································· 131
12.1.7 局所変数 ················································································································· 131
12.2 省略時の値 ················································································································ 132
12.3 確実な代入 ················································································································ 132
12.3.1 初期代入ありの変数 ·································································································· 133
12.3.2 初期代入なしの変数 ·································································································· 134
12.3.3 確実な代入を判断するための厳密な規則 ······································································· 134
12.3.3.1 文についての一般的な規則······················································································· 134
12.3.3.2 ブロック文,checked文及びunchecked文 ······························································ 135
12.3.3.3 式文 ···················································································································· 135
X 3015:2008 (ISO/IEC 23270:2006) 目次
(5)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
12.3.3.4 宣言文 ················································································································· 135
12.3.3.5 if文 ··················································································································· 135
12.3.3.6 switch文 ············································································································ 135
12.3.3.7 while文 ·············································································································· 136
12.3.3.8 do文 ··················································································································· 136
12.3.3.9 for文 ················································································································· 136
12.3.3.10 break文,continue文及びgoto文······································································ 137
12.3.3.11 throw文 ············································································································ 137
12.3.3.12 return文 ·········································································································· 137
12.3.3.13 try-catch文 ····································································································· 137
12.3.3.14 try-finally文 ································································································· 138
12.3.3.15 try-catch-finally文 ······················································································· 138
12.3.3.16 foreach文········································································································· 139
12.3.3.17 using文 ············································································································ 139
12.3.3.18 lock文 ·············································································································· 139
12.3.3.19 単純な式の一般的な規則 ························································································ 140
12.3.3.20 埋め込まれた式をもつ式の一般的な規則 ··································································· 140
12.3.3.21 呼出し式及びオブジェクト生成式 ············································································ 140
12.3.3.22 単純な代入式 ······································································································· 141
12.3.3.23 &&(二択条件論理積)式 ······················································································· 141
12.3.3.24 ||(二択条件論理和)式 ······················································································· 142
12.3.3.25 !式 ···················································································································· 143
12.3.3.26 ?:式 ·················································································································· 143
12.3.3.27 無名メソッド式 ···································································································· 144
12.3.3.28 yield文 ············································································································ 144
12.3.3.29 ??式 ·················································································································· 145
12.4 変数参照 ··················································································································· 145
12.5 変数参照の分割不能性 ································································································· 145
13 変換 ··························································································································· 145
13.1 暗黙の変換 ················································································································ 145
13.1.1 恒等変換 ················································································································· 146
13.1.2 暗黙の数値変換 ········································································································ 146
13.1.3 暗黙の列挙変換 ········································································································ 146
13.1.4 暗黙の参照変換 ········································································································ 147
13.1.5 ボックス化変換 ········································································································ 147
13.1.6 暗黙の型仮引数変換 ·································································································· 148
13.1.7 暗黙の定数式変換 ····································································································· 148
13.1.8 利用者定義の暗黙の変換 ···························································································· 148
13.2 明示的な変換 ············································································································· 149
X 3015:2008 (ISO/IEC 23270:2006) 目次
(6)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
13.2.1 明示的な数値変換 ····································································································· 149
13.2.2 明示的な列挙変換 ····································································································· 150
13.2.3 明示的な参照変換 ····································································································· 151
13.2.4 ボックス化解除変換 ·································································································· 152
13.2.5 明示的な型仮引数変換 ······························································································· 152
13.2.6 利用者定義の明示的な変換 ························································································· 153
13.3 標準変換 ··················································································································· 153
13.3.1 標準暗黙変換 ··········································································································· 153
13.3.2 標準明示変換 ··········································································································· 153
13.4 利用者定義変換 ·········································································································· 153
13.4.1 宣言可能な利用者定義変換 ························································································· 153
13.4.2 利用者定義変換の評価 ······························································································· 153
13.4.3 利用者定義の暗黙の変換 ···························································································· 155
13.4.4 利用者定義の明示的な変換 ························································································· 155
13.5 無名メソッド変換 ······································································································· 156
13.6 メソッドグループ変換 ································································································· 158
13.7 null許容型を伴う変換 ······························································································· 159
13.7.1 null型変換 ············································································································ 159
13.7.2 null許容変換 ········································································································· 159
13.7.3 もち上げられた変換 ·································································································· 160
14 式 ······························································································································ 160
14.1 式の分類 ··················································································································· 160
14.1.1 式の値 ···················································································································· 161
14.2 演算子 ······················································································································ 162
14.2.1 演算子の優先順位及び結合規則 ··················································································· 162
14.2.2 演算子多重定義 ········································································································ 163
14.2.3 単項演算子多重定義解決 ···························································································· 164
14.2.4 2項演算子多重定義解決 ···························································································· 164
14.2.5 利用者定義演算子候補 ······························································································· 165
14.2.6 数値昇格 ················································································································· 165
14.2.6.1 単項数値昇格 ········································································································ 166
14.2.6.2 2項数値昇格 ········································································································· 166
14.2.7 もち上げ演算子 ········································································································ 167
14.3 メンバ検索 ················································································································ 167
14.3.1 基底型 ···················································································································· 169
14.4 関数メンバ ················································································································ 169
14.4.1 実引数並び ·············································································································· 171
14.4.2 多重定義解決 ··········································································································· 173
14.4.2.1 適用可能な関数メンバ ···························································································· 174
X 3015:2008 (ISO/IEC 23270:2006) 目次
(7)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
14.4.2.2 より適切な関数メンバ ···························································································· 174
14.4.2.3 より適切な変換 ····································································································· 175
14.4.3 関数メンバ呼出し ····································································································· 176
14.4.3.1 ボックス化されたインスタンスの呼出し ····································································· 177
14.5 一次式 ······················································································································ 177
14.5.1 リテラル ················································································································· 178
14.5.2 単純名 ···················································································································· 178
14.5.2.1 ブロック中の不変な意味·························································································· 180
14.5.3 括弧付き式 ·············································································································· 181
14.5.4 メンバアクセス ········································································································ 181
14.5.4.1 型名と同じ単純名 ·································································································· 183
14.5.5 呼出し式 ················································································································· 183
14.5.5.1 メソッド呼出し ····································································································· 184
14.5.5.2 委譲の呼出し ········································································································ 185
14.5.6 要素アクセス ··········································································································· 185
14.5.6.1 配列アクセス ········································································································ 186
14.5.6.2 添字子アクセス ····································································································· 186
14.5.7 thisアクセス ········································································································· 187
14.5.8 baseアクセス ········································································································· 187
14.5.9 後置増加演算子及び後置減少演算子 ············································································· 188
14.5.10 new演算子 ············································································································ 189
14.5.10.1 オブジェクト生成式 ······························································································ 189
14.5.10.2 配列生成式 ·········································································································· 191
14.5.10.3 委譲生成式 ·········································································································· 192
14.5.11 typeof演算子 ······································································································· 195
14.5.12 sizeof演算子 ······································································································· 198
14.5.13 checked演算子及びunchecked演算子 ···································································· 198
14.5.14 省略時値式 ············································································································ 201
14.5.15 無名メソッド ········································································································· 201
14.5.15.1 無名メソッド呼出し情報 ························································································ 202
14.5.15.2 無名メソッドブロック ··························································································· 202
14.5.15.3 外変数 ················································································································ 203
14.5.15.3.1 外変数の捕そく(捉) ························································································· 203
14.5.15.3.2 局所変数のインスタンス化 ··················································································· 203
14.5.15.4 無名メソッドの評価 ······························································································ 206
14.5.15.5 実装例 ················································································································ 207
14.6 単項式 ······················································································································ 210
14.6.1 単項プラス演算子 ····································································································· 210
14.6.2 単項マイナス演算子(符号反転) ················································································ 210
X 3015:2008 (ISO/IEC 23270:2006) 目次
(8)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
14.6.3 論理否定演算子 ········································································································ 211
14.6.4 ビット単位の補数演算子 ···························································································· 211
14.6.5 前置増加演算子及び前置減少演算子 ············································································· 212
14.6.6 キャスト式 ·············································································································· 213
14.7 算術式 ······················································································································ 213
14.7.1 乗算演算子 ·············································································································· 214
14.7.2 除算演算子 ·············································································································· 215
14.7.3 剰余演算子 ·············································································································· 216
14.7.4 加算演算子 ·············································································································· 217
14.7.5 減算演算子 ·············································································································· 219
14.8 シフト演算子 ············································································································· 222
14.9 関係演算子及び型試験演算子 ························································································ 223
14.9.1 整数比較演算子 ········································································································ 223
14.9.2 浮動小数点数比較演算子 ···························································································· 225
14.9.3 10進実数比較演算子 ································································································· 226
14.9.4 論理型等価演算子 ····································································································· 226
14.9.5 列挙型比較演算子 ····································································································· 226
14.9.6 参照型等価演算子 ····································································································· 227
14.9.7 文字列等価演算子 ····································································································· 229
14.9.8 委譲等価演算子 ········································································································ 229
14.9.9 等価演算子及びnull許容型 ······················································································ 230
14.9.10 is演算子 ·············································································································· 230
14.9.11 as演算子 ·············································································································· 231
14.10 論理演算子 ·············································································································· 232
14.10.1 整数論理演算子 ······································································································ 233
14.10.2 列挙論理演算子 ······································································································ 233
14.10.3 真理値論理演算子 ··································································································· 234
14.10.4 bool?論理演算子 ··································································································· 234
14.11 二択条件論理演算子 ··································································································· 234
14.11.1 真理値二択条件論理演算子 ······················································································· 235
14.11.2 利用者定義の二択条件論理演算子··············································································· 235
14.12 null判定選択演算子································································································· 236
14.13 二択条件演算子 ········································································································ 237
14.14 代入演算子 ·············································································································· 237
14.14.1 単純代入 ··············································································································· 238
14.14.2 複合代入 ··············································································································· 240
14.14.3 イベント代入 ········································································································· 241
14.15 式 ·························································································································· 241
14.16 定数式 ···················································································································· 241
X 3015:2008 (ISO/IEC 23270:2006) 目次
(9)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
14.17 真理式 ···················································································································· 243
15 文 ······························································································································ 243
15.1 終了点及び到達可能性 ································································································· 244
15.2 ブロック ··················································································································· 245
15.2.1 文並び ···················································································································· 246
15.3 空文 ························································································································· 246
15.4 ラベル付き文 ············································································································· 246
15.5 宣言文 ······················································································································ 247
15.5.1 局所変数宣言 ··········································································································· 247
15.5.2 局所定数宣言 ··········································································································· 248
15.6 式文 ························································································································· 249
15.7 選択文 ······················································································································ 249
15.7.1 if文 ····················································································································· 249
15.7.2 switch文 ·············································································································· 250
15.8 繰返し文 ··················································································································· 254
15.8.1 while文 ················································································································ 254
15.8.2 do文 ····················································································································· 254
15.8.3 for文 ··················································································································· 255
15.8.4 foreach文 ············································································································ 256
15.9 飛越し文 ··················································································································· 259
15.9.1 break文 ················································································································ 260
15.9.2 continue文··········································································································· 260
15.9.3 goto文 ·················································································································· 261
15.9.4 return文 ·············································································································· 262
15.9.5 throw文 ················································································································ 263
15.10 try文 ···················································································································· 263
15.11 checked文及びunchecked文 ··················································································· 266
15.12 lock文 ·················································································································· 267
15.13 using文 ················································································································· 267
15.14 yield文 ················································································································· 269
16 名前空間 ····················································································································· 271
16.1 コンパイル単位 ·········································································································· 271
16.2 名前空間宣言 ············································································································· 272
16.3 外部別名指令 ············································································································· 273
16.4 using指令 ··············································································································· 274
16.4.1 using別名指令 ······································································································· 274
16.4.2 using名前空間指令 ································································································· 279
16.5 名前空間メンバ ·········································································································· 281
16.6 型宣言 ······················································································································ 281
X 3015:2008 (ISO/IEC 23270:2006) 目次
(10)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
16.7 限定別名メンバ ·········································································································· 282
17 クラス ························································································································ 284
17.1 クラス宣言 ················································································································ 284
17.1.1 クラス修飾子 ··········································································································· 284
17.1.1.1 抽象クラス ··········································································································· 285
17.1.1.2 封印クラス ··········································································································· 286
17.1.1.3 静的クラス ··········································································································· 286
17.1.2 クラス基底指定 ········································································································ 287
17.1.2.1 基底クラス ··········································································································· 288
17.1.2.2 インタフェース実装 ······························································································· 289
17.1.3 クラス本体 ·············································································································· 290
17.1.4 部分宣言 ················································································································· 290
17.2 クラスメンバ ············································································································· 291
17.2.1 継承 ······················································································································· 294
17.2.2 new修飾子 ············································································································· 295
17.2.3 アクセス修飾子 ········································································································ 295
17.2.4 成分要素型 ·············································································································· 295
17.2.5 静的メンバとインスタンスメンバ ················································································ 295
17.2.6 入れ子型 ················································································································· 297
17.2.6.1 完全限定名 ··········································································································· 297
17.2.6.2 宣言されたアクセス可能性······················································································· 297
17.2.6.3 隠ぺい ················································································································· 298
17.2.6.4 thisアクセス ······································································································ 299
17.2.6.5 包含する型の非公開メンバ及び限定公開メンバへのアクセス ·········································· 300
17.2.7 予約されたメンバ名 ·································································································· 301
17.2.7.1 特性用に予約されたメンバ名···················································································· 301
17.2.7.2 イベント用に予約されたメンバ名 ·············································································· 302
17.2.7.3 添字子用に予約されたメンバ名 ················································································· 302
17.2.7.4 終了化子用に予約されたメンバ名 ·············································································· 303
17.3 定数 ························································································································· 303
17.4 フィールド ················································································································ 305
17.4.1 静的フィールド及びインスタンスフィールド ································································· 306
17.4.2 読込み専用フィールド ······························································································· 306
17.4.2.1 定数に静的読込み専用フィールドを利用する ······························································· 306
17.4.2.2 定数及び静的読込みフィールドの版管理 ····································································· 307
17.4.3 揮発性フィールド ····································································································· 308
17.4.4 フィールド初期化 ····································································································· 309
17.4.5 変数初期化子 ··········································································································· 310
17.4.5.1 静的フィールド初期化 ···························································································· 311
X 3015:2008 (ISO/IEC 23270:2006) 目次
(11)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
17.4.5.2 インスタンスフィールド初期化 ················································································· 312
17.5 メソッド ··················································································································· 313
17.5.1 メソッド仮引数 ········································································································ 314
17.5.1.1 値仮引数 ·············································································································· 315
17.5.1.2 参照仮引数 ··········································································································· 316
17.5.1.3 出力仮引数 ··········································································································· 317
17.5.1.4 仮引数配列 ··········································································································· 318
17.5.2 静的メソッド及びインスタンスメソッド ······································································· 321
17.5.3 仮想メソッド ··········································································································· 321
17.5.4 上書きメソッド ········································································································ 324
17.5.5 封印メソッド ··········································································································· 325
17.5.6 抽象メソッド ··········································································································· 326
17.5.7 外部メソッド ··········································································································· 328
17.5.8 メソッド本体 ··········································································································· 328
17.5.9 メソッド多重定義 ····································································································· 329
17.6 特性 ························································································································· 329
17.6.1 静的特性及びインスタンス特性 ··················································································· 330
17.6.2 アクセス子 ·············································································································· 331
17.6.3 virtual,sealed,override及びabstractのアクセス子 ········································· 338
17.7 イベント ··················································································································· 341
17.7.1 フィールドのように使用できるイベント ······································································· 343
17.7.2 イベントアクセス子 ·································································································· 347
17.7.3 静的イベント及びインスタンスイベント ······································································· 348
17.7.4 仮想,封印,上書き及び抽象のアクセス子 ···································································· 348
17.8 添字子 ······················································································································ 349
17.8.1 添字子の多重定義 ····································································································· 353
17.9 演算子 ······················································································································ 353
17.9.1 単項演算子 ·············································································································· 355
17.9.2 2項演算子 ·············································································································· 356
17.9.3 変換演算子 ·············································································································· 356
17.10 インスタンス構築子 ·································································································· 357
17.10.1 構築子初期化子 ······································································································ 359
17.10.2 インスタンス変数初期化子 ······················································································· 359
17.10.3 構築子の実行 ········································································································· 360
17.10.4 省略時構築子 ········································································································· 362
17.10.5 非公開構築子 ········································································································· 363
17.10.6 省略可能なインスタンス構築子の仮引数 ····································································· 363
17.11 静的構築子··············································································································· 363
17.12 終了化子 ················································································································· 366
X 3015:2008 (ISO/IEC 23270:2006) 目次
(12)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
18 構造体 ························································································································ 368
18.1 構造体宣言 ················································································································ 368
18.1.1 構造体修飾子 ··········································································································· 368
18.1.2 構造体インタフェース ······························································································· 369
18.1.3 構造体本体 ·············································································································· 369
18.2 構造体メンバ ············································································································· 369
18.3 クラスと構造体との相違 ······························································································ 369
18.3.1 値文脈 ···················································································································· 369
18.3.2 継承 ······················································································································· 370
18.3.3 代入 ······················································································································· 370
18.3.4 省略時の値 ·············································································································· 371
18.3.5 ボックス化及びボックス化解除 ··················································································· 371
18.3.6 thisの意味 ············································································································ 372
18.3.7 フィールド初期化子 ·································································································· 372
18.3.8 構築子 ···················································································································· 372
18.3.9 終了化子 ················································································································· 373
18.3.10 静的構築子 ············································································································ 373
19 配列 ··························································································································· 373
19.1 配列型 ······················································································································ 374
19.1.1 System.Array型 ··································································································· 374
19.2 配列の生成 ················································································································ 375
19.3 配列要素へのアクセス ································································································· 375
19.4 配列メンバ ················································································································ 375
19.5 配列の共変性 ············································································································· 375
19.6 配列と総称IListインタフェース ················································································· 376
19.7 配列初期化子 ············································································································· 377
20 インタフェース ············································································································ 378
20.1 インタフェース宣言 ···································································································· 378
20.1.1 インタフェース修飾子 ······························································································· 379
20.1.2 基底インタフェース ·································································································· 379
20.1.3 インタフェース本体 ·································································································· 381
20.2 インタフェースメンバ ································································································· 381
20.2.1 インタフェースのメソッド ························································································· 382
20.2.2 インタフェースの特性メンバ ······················································································ 382
20.2.3 インタフェースのイベント ························································································· 383
20.2.4 インタフェースの添字子 ···························································································· 383
20.2.5 インタフェースのメンバアクセス ················································································ 383
20.3 完全限定されたインタフェースメンバ名 ········································································· 385
20.4 インタフェースの実装 ································································································· 386
X 3015:2008 (ISO/IEC 23270:2006) 目次
(13)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
20.4.1 明示的なインタフェースメンバの実装 ·········································································· 387
20.4.2 インタフェース写像 ·································································································· 390
20.4.3 インタフェース実装の継承 ························································································· 393
20.4.4 インタフェースの再実装 ···························································································· 395
20.4.5 抽象クラス及びインタフェース ··················································································· 397
21 enum(列挙) ·············································································································· 397
21.1 列挙宣言 ··················································································································· 398
21.2 列挙修飾子 ················································································································ 398
21.3 列挙メンバ ················································································································ 399
21.4 System.Enum型 ······································································································· 401
21.5 列挙値と演算 ············································································································· 401
22 委譲 ··························································································································· 402
22.1 委譲宣言 ··················································································································· 402
22.2 委譲の具現化 ············································································································· 404
22.3 委譲による呼出し ······································································································· 405
23 例外 ··························································································································· 407
23.1 例外の原因 ················································································································ 408
23.2 System.Exceptionクラス ························································································ 408
23.3 例外をどのように扱うか ······························································································ 408
23.4 よくある例外クラス ···································································································· 409
24 属性 ··························································································································· 409
24.1 属性クラス ················································································································ 410
24.1.1 属性の用法 ·············································································································· 410
24.1.2 順序指定仮引数及び名前付き仮引数 ············································································· 411
24.1.3 属性仮引数型 ··········································································································· 412
24.2 属性の指定 ················································································································ 412
24.3 属性インスタンス ······································································································· 418
24.3.1 属性のコンパイル ····································································································· 418
24.3.2 属性インスタンスの実行時取出し ················································································ 419
24.4 予約された属性 ·········································································································· 420
24.4.1 AttributeUsage属性 ····························································································· 420
24.4.2 Conditional属性 ·································································································· 420
24.4.2.1 Conditional属性 ································································································ 420
24.4.2.2 条件付き属性クラス ······························································································· 423
24.4.3 Obsolete属性········································································································ 424
25 総称性 ························································································································ 425
25.1 総称クラス宣言 ·········································································································· 425
25.1.1 型仮引数 ················································································································· 426
25.1.2 インスタンス型 ········································································································ 427
X 3015:2008 (ISO/IEC 23270:2006) 目次
(14)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
25.1.3 総称クラスのメンバ ·································································································· 427
25.1.4 総称クラスにおける静的フィールド ············································································· 428
25.1.5 総称クラスにおける静的構築子 ··················································································· 428
25.1.6 限定公開メンバへのアクセス ······················································································ 429
25.1.7 総称クラスにおける多重定義 ······················································································ 430
25.1.8 仮引数配列をもつメソッド及び型仮引数 ······································································· 431
25.1.9 上書き及び総称クラス ······························································································· 431
25.1.10 総称クラスにおける演算子 ······················································································· 432
25.1.11 総称クラスにおける入れ子型 ···················································································· 434
25.2 総称構造体宣言 ·········································································································· 435
25.3 総称インタフェース宣言 ······························································································ 435
25.3.1 実装されたインタフェースの一意性 ············································································· 436
25.3.2 インタフェースメンバの明示的な実装 ·········································································· 437
25.4 総称委譲宣言 ············································································································· 437
25.5 構築型 ······················································································································ 438
25.5.1 型実引数 ················································································································· 439
25.5.2 開型及び閉型 ··········································································································· 439
25.5.3 構築型の基底クラス及びインタフェース ······································································· 440
25.5.4 構築型のメンバ ········································································································ 440
25.5.5 構築型のアクセス可能性 ···························································································· 441
25.5.6 変換 ······················································································································· 441
25.5.7 using別名指令 ······································································································· 442
25.6 総称メソッド ············································································································· 442
25.6.1 総称メソッド呼出し情報 ···························································································· 443
25.6.2 仮想総称メソッド ····································································································· 443
25.6.3 総称メソッドの呼出し ······························································································· 445
25.6.4 型実引数に関する推論 ······························································································· 446
25.6.5 委譲を伴った総称メソッドの使用 ················································································ 447
25.6.6 総称特性,総称イベント,総称添字子,総称演算子,総称構築子及び総称終了化子の禁止 ····· 448
25.7 制約 ························································································································· 448
25.7.1 制約の充足 ·············································································································· 453
25.7.2 型仮引数が関係するメンバ検索 ··················································································· 455
25.7.3 型仮引数及びボックス化 ···························································································· 455
25.7.4 型仮引数を含んだ変換 ······························································································· 457
26 反復子 ························································································································ 458
26.1 反復子ブロック ·········································································································· 458
26.1.1 列挙子インタフェース ······························································································· 459
26.1.2 列挙可能インタフェース ···························································································· 459
26.1.3 産出型 ···················································································································· 459
X 3015:2008 (ISO/IEC 23270:2006) 目次
(15)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ページ
26.1.4 thisによるアクセス ································································································ 459
26.2 列挙子オブジェクト ···································································································· 459
26.2.1 MoveNextメソッド·································································································· 460
26.2.2 Current特性 ········································································································· 461
26.2.3 Disposeメソッド ··································································································· 461
26.3 列挙可能オブジェクト ································································································· 462
26.3.1 GetEnumeratorメソッド ························································································· 462
26.4 実装例 ······················································································································ 462
27 安全でないコード ········································································································· 470
27.1 安全でない文脈 ·········································································································· 471
27.2 ポインタ型 ················································································································ 474
27.3 固定変数及び移動可能変数 ··························································································· 476
27.4 ポインタ変数 ············································································································· 477
27.5 式中のポインタ ·········································································································· 478
27.5.1 ポインタ間接参照 ····································································································· 479
27.5.2 ポインタメンバアクセス ···························································································· 479
27.5.3 ポインタ要素アクセス ······························································································· 480
27.5.4 アドレス参照演算子 ·································································································· 481
27.5.5 ポインタ加算及び減算 ······························································································· 482
27.5.6 ポインタ算術 ··········································································································· 482
27.5.7 ポインタ比較 ··········································································································· 484
27.5.8 sizeof演算子 ········································································································ 484
27.6 fixed文 ·················································································································· 484
27.7 スタック割当て ·········································································································· 488
27.8 動的メモリ割当て ······································································································· 489
附属書A(参考)文法 ········································································································· 492
附属書B(参考)移植性の問題点··························································································· 527
附属書C(参考)名前付けの指針 ·························································································· 529
附属書D(規定)標準ライブラリ ·························································································· 530
附属書E(参考)文書化注釈 ································································································ 544
附属書F(参考)参考文献 ···································································································· 569
X 3015:2008 (ISO/IEC 23270:2006) 目次
(16)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
まえがき
この規格は,工業標準化法第14条によって準用する第12条第1項の規定に基づき,社団法人情報処理
学会情報規格調査会(ITSCJ)及び財団法人日本規格協会(JSA)から,工業標準原案を具して日本工業規格を
改正すべきとの申出があり,日本工業標準調査会の審議を経て,経済産業大臣が改正した日本工業規格で
ある。
これによって,JIS X 3015:2005は改正され,この規格に置き換えられた。
この規格は,著作権法で保護対象となっている著作物である。
この規格の一部が,特許権,出願公開後の特許出願,実用新案権又は出願公開後の実用新案登録出願に
抵触する可能性があることに注意を喚起する。経済産業大臣及び日本工業標準調査会は,このような特許
権,出願公開後の特許出願,実用新案権又は出願公開後の実用新案登録出願に係る確認について,責任は
もたない。
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
日本工業規格 JIS
X 3015:2008
(ISO/IEC 23270:2006)
プログラム言語C#
Information technology−Programming languages−C#
序文
この規格は,2006年に第2版として発行されたISO/IEC 23270を基に,技術的内容及び対応国際規格の
構成を変更することなく作成した日本工業規格である。
なお,この規格で点線の下線を施してある参考事項は,対応国際規格にはない事項である。
1
適用範囲
この規格は,プログラム言語C#(シーシャープ)で書かれたプログラムの形式及びその解釈を規定する。
すなわち,この規格は,次を規定する。
− C#プログラムの表現。
− C#言語の構文及び制約。
− C#プログラムを解釈するための意味規則。
− C#の適合実装に課す制限及び限界。
この規格は,次を規定しない。
− データ処理システムによる利用のためにC#プログラムが変換される機構。
− データ処理システムによる利用のためにC#アプリケーションが呼び出される機構。
− C#アプリケーションによる利用のために入力データが変換される機構。
− C#アプリケーションが生成した後に出力データが変換される機構。
− 特定のデータ処理システムの容量又は特定のプロセッサの容量を超える,プログラム及びそのデータ
の,サイズ又は計算量。
− 適合実装を利用可能とするデータ処理システムへの最低限度の要件。
注記 この規格の対応国際規格及びその対応の程度を表す記号を,次に示す。
ISO/IEC 23270:2006,Information technology−Programming languages−C# (IDT)
なお,対応の程度を表す記号(IDT)は,ISO/IEC Guide 21に基づき,一致していることを示す。
2
適合性
適合性は,次の人々にかかわる。
− C#実装の設計,実装又は保守の担当者。
− C#実装を入手しようとする政府又は市場の関係者。
− C#適合試験パッケージを提供しようとする試験組織。
− あるC#実装から別のC#実装へコードを移植しようとするプログラマ。
− 標準C#を教えようとする教育者。
2
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 標準C#について書こうとする著者。
このように,適合性は非常に重要であって,この規格の大部分は,C#実装及びC#プログラムを規格適
合とする特性を規定することを意図している。
注記 この規格において,対応国際規格においては太字で表されている強調部分には下線を施す。
この規格において,それらの要件を規定する本文が規定と考えられる。この規格において,それ以外の
本文は,参考となる。すなわち,付加的情報だけを与える。特に断らない限り,すべての本文は規定とす
る。規定本文は,更に,必す(須)及び条件付きに分かれる。条件付き規定本文は,省略可能な機能及び
その要件を規定する。しかし,その機能が提供される場合には,その構文及び意味は規定されたとおりで
なければならない。
この規格の要件に違反したときの振る舞いは定義しない。この規格において,未定義の動作は,“未定義
の動作”という用語によってだけ示される。
厳格に適合するプログラムは,この規格で必す(須)とされた機能だけを使う(これは,厳格に適合す
るプログラムが条件付きで規定された機能を一切使えないことを意味する。)。厳格に適合するプログラム
は,いかなる未規定,未定義又は実装定義の動作に依存する出力も生成してはならない。
C#の適合実装は,あらゆる厳格に適合するプログラムを受理しなければならない。
C#の適合実装は,この規格で規定された(条件付き規定を除く。)すべての型,値,オブジェクト,特
性,メソッド並びにプログラムの構文及びプログラムの意味を提供して使えるようにしなければならない。
C#の適合実装は,JIS X 0221及びUnicode標準4.0版に適合する文字を解釈しなければならない。適合
実装は,UTF-8符号化形式で符号化されたUnicodeソースファイルを受理しなければならない。
C#の適合実装は,#error前処理指令を含むソースコードの翻訳に,それが条件付きコンパイルによっ
て読み飛ばされる部分でない限り,成功してはならない。
C#の適合実装は,ソースプログラムが構文規則に違反するか,又は(“しなければならない”,“しては
ならない”,“エラー”又は“警告”と定義された)否定的要件に違反した場合には,その要件に“診断メ
ッセージ出力は不要とする。”という規定がない限りは,少なくとも一つの診断メッセージを出力しなけれ
ばならない。
C#の適合実装は,厳格に適合したいかなるプログラムの振る舞いをも変えない限り,この規格で示され
ていない追加的な型,値,オブジェクト,特性及びメソッドを提供してもよい。適合実装は,この規格に
違反する拡張機能を使うプログラムを診断できなければならない。ただし,診断の後で,そのようなプロ
グラムをコンパイルし実行してもよい(拡張できるということは,適合実装は,この規格で明示的に予約
した識別子を除き,いずれの識別子も予約していないことを意味する。)。
C#の適合実装は,すべての実装定義の特徴及びすべての拡張を定義する文書を用意しなければならない。
C#の適合実装は,附属書Dに示されたクラスライブラリを提供しなければならない。この規格では,そ
のクラスライブラリを参照する。
適合プログラムとは,適合実装で受理されるプログラムとする(そのようなプログラムには,拡張又は
条件付きで規定された機能が含まれてもよい。)。
3
引用規格
次に掲げる規格は,この規格に引用されることによって,この規格の規定の一部を構成する。これらの
引用規格のうちで,西暦年を付記してあるものは,記載の年の版を適用し,その後の改正版(追補を含む。)
には適用しない。西暦年の付記がない引用規格は,その最新版(追補を含む。)を適用する。
3
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
JIS X 0001:1994 情報処理用語−基本用語
注記 対応国際規格:ISO/IEC 2382-1:1993,Information technology−Vocabulary−Part 1: Fundamental
terms (MOD)
JIS X 0221 国際符号化文字集合 (UCS)
注記 対応国際規格:ISO/IEC 10646:2003,Information technology−Universal Multiple−Octet Coded
Character Set (UCS),Amd.1:2005及びAmd.2:2006 (IDT)
JIS Z 8401:1999 数値の丸め方
ISO 31-11:1992 Quantities and units−Part 11: Mathematical signs and symbols for use in the physical
sciences and technology.
注記 JIS Z 8201 数学記号はISO 31-11:1978に対応する(MOD)
ISO/IEC 23271:2006 Information technology−Common Language Infrastructure (CLI) Partitions I to VI
注記 JIS X 3016:2006 共通言語基盤 (CLI) がISO/IEC 23271:2003 Information technology−
Common Language Infrastructureに対応している。(IDT)
IEC 60559:1989 Binary floating-point arithmetic for microprocessor systems
注記 従前の番号は,IEC 559:1989(この規格は,米国標準ANSI/IEEE Standard 754-1985,IEEE
Standard for Binary Floating-Point Arithmeticとしてよく知られている。)。
The Unicode Standard, Version 4.0, The Unicode Consortium, (Addison-Wesley, Boston, MA, 2003. ISBN
0-321-18578-1).
4
用語及び定義
この規格では,次の定義を用いる。この他の用語は,≪≫でくくられたもの,すなわち,構文規則で左
辺に出てきたときに定義される。この規格で明示的に定義された用語は,他で定義された同様の用語と混
同してはならない。この規格で定義されない用語は,JIS X 0001〜0032に従って解釈する。この規格で定
義されない数学記号は,JIS Z 8201に従って解釈する。
4.1
アプリケーション (Application)
入口点をもつアセンブリ(10.1参照)。アプリケーション実行に際しては,新しいアプリケーション領域
が生成される。同一のアプリケーションに対する複数の異なるインスタンスが,同一コンピュータ上に同
時に具現化できる。このとき,それぞれのインスタンスは,独自のアプリケーション領域をもつ。
4.2
アプリケーション領域 (Application domain)
アプリケーションの状態のコンテナとして働くことによって,アプリケーションの隔離を実現するもの。
アプリケーション領域は,アプリケーションで定義された型及びアプリケーションで使用するクラスライ
ブラリのためのコンテナ及び境界として機能する。あるアプリケーション領域にロードされた型は,別の
アプリケーション領域にロードされた同じ型とは区別される。インスタンスを直接共有することはできな
い。例えば,アプリケーション領域ごとに,これらの型の静的変数の複製をもつ。また,型の静的構築子
は,アプリケーション領域ごとに高々1回しか実行されない。実装は,アプリケーション領域の構築及び
破棄に関する,実装固有の方針又は機構を提供してよい。
4
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
4.3
引数 (Argument)
メソッド呼出し式又はインスタンス構築子呼出し式において,コンマで区切られ括弧で囲まれた並び,
又は要素アクセス式において,コンマで区切られ角括弧で囲まれた並びで表された式。実引数とも呼ばれ
る。
4.4
アセンブリ (Assembly)
プログラムコンパイルの結果としてコンパイラによって出力される一つ以上のファイル。アセンブリは,
ロード可能なコードモジュールの集合と,それとともに一つの機能単位を実装する他の資源とから構成さ
れる。アセンブリは,型,型を実装するために使われる実行可能コード及び他のアセンブリに対する参照
を含むことができる。この規格では,アセンブリの物理的表現を定義しない。アセンブリはコンパイラの
出力である。
4.5
振る舞い (Behavior)
外からの見え方,すなわち動作。
4.6
実装定義の振る舞い (Implementation-defined behavior)
それぞれの実装がどのような選択をしたか文書に記す未規定の振る舞い。
4.7
未定義の振る舞い (Undefined behavior)
この規格では一切要件を課さない,非可搬な若しくは誤りのある構成要素,又は誤りのあるデータの使
用によって生じる振る舞い。
注記 未定義の振る舞いの処理の可能性は,その状況を完全に無視してどのような結果になっても放
っておくものから,その環境において文書化されている方式に従って(診断メッセージを出す
ものもあれば出さないものもあり)実行若しくはコンパイルするやり方,又は(診断メッセー
ジを出して)実行若しくはコンパイルを停止するものまで広範囲にわたる。
4.8
未規定の振る舞い (Unspecified behavior)
この規格が,二つ以上の可能性を与えており,どのインスタンスについてもそれ以上の要件を課してい
ない振る舞い。
4.9
クラスライブラリ (Class library)
他のアセンブリで使用できるアセンブリ。クラスライブラリの使用は,新しいアプリケーション領域の
生成をもたらさない。クラスライブラリは,それを使うアプリケーション領域にロードされる。例えば,
アプリケーションがクラスライブラリを使うときには,そのアプリケーションのアプリケーション領域に
ロードされる。アプリケーションが,それ自体でクラスライブラリBを使っているクラスライブラリAを
使う場合,A及びBの両方がそのアプリケーションのアプリケーション領域にロードされる。
4.10
診断メッセージ (Diagnostic message)
実装の出力メッセージの中で,実装定義の部分に属するメッセージ。
5
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
4.11
コンパイル時エラー (Compile-time error)
プログラムのコンパイルで報告されるエラー。
4.12
例外 (Exception)
通常期待される振る舞いの範囲外でのエラー条件。
4.13
実装 (Implementation)
特定の実行環境のためにプログラムのコンパイルを実行し,その環境でのメソッドの実行を行う,(特定
の任意制御機能のもとにあって特定のコンパイル環境において動作する)あるソフトウェアの集合。
4.14
名前空間 (Namespace)
他のプログラムに対して提供されるプログラム要素の提示方式を提供する論理的組織体系。
4.15
仮引数 (Parameter)
メソッド,インスタンス構築子,演算子又は添字子定義の一部として宣言され,その関数メンバへの入
口で値をとる変数。形式仮引数ともいう。
4.16
プログラム (Program)
コンパイラに提示された一つ以上のソースファイル。プログラムは,コンパイラへの入力となる。
4.17
正当なプログラム (Valid program)
構文規則及び診断可能な意味規則に従って構築されたC#プログラム。
4.18
プログラム具現化 (Program instantiation)
アプリケーションの実行。
4.19
推奨されたやり方 (Recommended practice)
ある種の実装では実際的ではないかもしれないが,規格の意図に沿って強く推奨される規定。
4.20
ソースファイル (Source file)
Unicode文字の順序だった列。ソースファイルは,普通,ファイルシステムのファイルと一対一に対応
するが,この対応は,必す(須)ではない。
4.21
安全でないコード (Unsafe code)
ポインタの宣言及び操作,ポインタと整数型との間の変換,並びに変数のアドレス取得のような低水準
操作の実行を許すコード。このような操作は,基盤オペレーティングシステムとのインタフェース,メモ
リマップ機器へのアクセス及び時間に厳しいアルゴリズムの実装といった機能を提供する。
4.22
コンパイル時警告 (Compile-time warning)
6
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
プログラム要素の問題があるかもしれない使い方の特定を意図して,プログラムコンパイル中に報告さ
れる参考メッセージ。
5
表記法
C#の字句文法及び構文文法は,この規格の諸箇条で示される。字句文法は,文字をどのように組み合わ
せれば,言語の最小字句的要素である字句(トークン,9.4参照)になるかを定義する。構文文法は,字句
をどのように組み合わせれば,正当なC#プログラムになるかを定義する。
文法規則は,終端記号及び非終端記号の両方を含む。文法規則において,非終端記号は,≪≫で囲み,
終端記号は,タイプライタ体で示す。非終端記号は,一連の文法規則で定義される。文法規則の第1行は,
左辺にまず非終端記号の名前が来て,次に一つ又は二つのコロンが続く。一つのコロンは構文文法に用い,
二つのコロンは字句文法に用いる。2行目以下は,第1行の非終端記号に対する右辺となる。次に例を示
す。
≪クラス修飾子≫:
new
public
protected
internal
private
abstract
sealed
static
これは,七つの規則をもつ非終端≪クラス修飾子≫を定義する。
選択可能な複数の規則は,通常,このように行を分けて示すが,個数が多い場合には,“次のいずれか”
という句を用い,その後に候補を並べる。これは,規則をそれぞれ別の行に記述する代わりに省略表現し
ただけである。次に例を示す。
≪10進数字≫: 次のいずれか
0 1 2 3 4 5 6 7 8 9
上の式は,下の式と同等とする。
≪10進数字≫:
0
1
2
3
4
5
6
7
8
9
≪識別子≫optのように下付きの接尾辞“opt”を,省略可能な記号を示すための略記として用いる。次に
7
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例を示す。
≪for文≫:
for ( ≪for初期化子≫opt ; ≪for条件≫opt ; ≪for反復子≫opt ) ≪埋込み文≫
この規則は,次の規則と同等になる。
≪for文≫:
for ( ; ; ) ≪埋込み文≫
for ( ≪for初期化子≫; ; ) ≪埋込み文≫
for ( ; ≪for条件≫ ; ) ≪埋込み文≫
for ( ; ; ≪for反復子≫) ≪埋込み文≫
for ( ≪for初期化子≫ ; ≪for条件≫ ; ) ≪埋込み文≫
for ( ; ≪for条件≫ ; ≪for反復子≫ ) ≪埋込み文≫
for ( ≪for初期化子≫ ; ; ≪for反復子≫ ) ≪埋込み文≫
for ( ≪for初期化子≫ ; ≪for条件≫ ; ≪for反復子≫ ) ≪埋込み文≫
すべての終端文字は,異なるUnicode文字範囲からの文字が同じように見えたとしても,U+0020〜
U+007Fの範囲の適切なUnicode文字とする。
注記 U+0020〜U+007Fの範囲のUnicode文字は,いわゆるASCII英文字に相当する。
6
頭字語及び略語
箇条6は,参考であって,規定ではない。
この規格では,次の頭字語及び略語を用いる。
BCL - 基本クラスライブラリ (Base Class Library)。これは,CLIの組込みデータ型を表現する型,単純
なファイルアクセス,カスタム属性,セキュリティ属性,文字列操作,書式処理,ストリーム及びゴミ集
めを提供する。
CLI - 共通言語基盤 (Common Language Infrastructure)。
CLS - 共通言語規定 (Common Language Specification)。
IEC - 国際電気標準会議 (the International Electrotechnical Commission)。
IEEE - 電気電子技術者協会 (the Institute of Electrical and Electronics Engineers)。
ISO - 国際標準化機構 (the International Organization for Standardization)。
C#は,“しーしゃーぷ”と発音する。
C#は,LATIN CAPITAL LETTER C (U+0043)の次にNUMBER SIGN # (U+0023)を書く。
7
規格概説
注記 この部分は,規定ではない。
この規格は,実装担当者,教育関係者及びアプリケーションプログラマが使うことを想定する。したが
って,厳密にいえば,形式言語仕様で必要としない説明的な内容も含む。
この規格の内容は,次の四つに分けられる。
1) 冒頭(箇条1〜箇条7)。
2) 言語の概要(箇条8)。
3) 言語の構文,制約及び意味(箇条9〜箇条27)。
4) 附属書(附属書A〜附属書F)。
8
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
記述された構成にどのようなものがあるかを説明するために例を与える。関連する箇条を示すために参
照を用いる。実装担当者又はプログラマのための助言又は指導として注記を与える。附属書は,この規格
に含まれる情報のまとめ及び追加情報を与える。
箇条1〜箇条5,箇条7の一部,箇条9〜箇条26,箇条27の冒頭及び附属書Dのほとんどは,この規格
の規定とする。冒頭を除く箇条27は,条件付き規定とする。箇条6,箇条7の一部,箇条8,附属書A,
B,C,E,F及びDの一部,注記,例並びに索引は,規定ではない。
全体が規定でないと明示された箇条及び附属書を除けば,規定本文内に含まれる規定でない箇所は,次
のいずれかで示される。
1) 次によって区切られた附属書の一部。
参考 この部分は,規定ではない。
...
参考 終わり。
2) 例 次の例は,…コード断片,説明が続くことがある。
3) 注記 説明
参考として記されていないすべての文は,規定とする。
8
言語概要
箇条8は参考であって,規定ではない。
C#(“しーしゃーぷ”と読む。)は,オブジェクト指向及び型安全という特徴を備えた最新の複雑でない
プログラム言語である。C及びC++のプログラマは,すぐにC#を習得できる。C#は,RAD(迅速なアプ
リケーション開発)言語の生産性とC++の威力とを融合する。
箇条8の残りは,言語の基本機能を示す。箇条9〜箇条27では,規則及び例外について詳細に,場合に
よっては数学的に示す。箇条8では,明確さ及び簡潔さを優先する。手軽なプログラムが書けて,箇条9
〜箇条27の内容を理解しやすいように,言語の概要を示す。
8.1
プログラム例
よく使われる“hello, world”プログラムは,次のように記述する。
using System;
class Hello
{
static void Main() {
Console.WriteLine("hello, world");
}
}
C#プログラムのソースコードは,通常,hello.csのようなファイル拡張子.csをもつ一つ以上のテキ
ストファイルに格納される。プログラムは,コマンド行コンパイラを用いて次のようにコンパイルされる。
csc hello.cs
これは,hello.exeという名前のアプリケーションを生成する。このアプリケーションを実行したと
きに生成される出力は次となる。
hello, world
このプログラムについて,次の四つが分かる。
9
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− using System;という指令は,共通言語基盤 (CLI) クラスライブラリが提供する名前空間System
を参照する。この名前空間は,メソッドMain中で参照されるクラスConsoleを含む。名前空間は,
プログラムの要素を階層的に構成する手段を提供する。using指令を使うと,その名前空間の要素で
あるクラスを,名前空間の限定を付けずに使用できる。“hello, world”プログラムは,
Console.WriteLineをSystem.Console.WriteLineの代わりに使っている。
− メソッドMainは,クラスHelloのメンバである。これには,修飾子staticがあるので,クラスの
インスタンスメソッドではなくクラスHelloのメソッドとなる。
− 実行開始のために呼ばれるメソッドであるアプリケーションの入口点は,常に,Mainと名前付けら
れた静的メソッドである。
− 出力“hello, world”は,クラスライブラリを使って生成される。この規格には,クラスライブラリを
含まない。その代わりに,CLIが提供するクラスライブラリを参照する。
C及びC++ の開発者にとっては,“hello, world”プログラムに現れない次の四つが興味深い。
− このプログラムは,Mainを大域メソッドとして使っていない。メソッド及び変数は,大域的には使
えない。この種の要素は常に(クラス及び構造体の宣言などの)型宣言中に含まれる。
− このプログラムは,演算子“::”又は“->”を使っていない。“::”という字句は,名前空間の別名
を名前空間のメンバと分離するときだけに使われる。演算子“->”は,(安全でないコードを含む)
一部のプログラムでだけ使われる。分離子“.”は,Console.WriteLineのような複合名で使われ
る。
− このプログラムは,事前宣言を含まない。宣言の順序が問題とならないので,事前宣言は全く必要な
い。
− このプログラムは,プログラムテキストの移入に#includeを使っていない。プログラム間の依存関
係は,字面ではなく記号的に処理される。この方式は,異なる言語で作成されたアプリケーション間
の障壁を取り除く。例えば,クラスConsoleは,C#で書かれる必要がない。
8.2
型
C#では,値型及び参照型という2種類の型が使える。値型は,単純型(char,int及びfloat),列挙
型及び構造体型を含む。参照型は,クラス型,インタフェース型,委譲型及び配列型を含む。
値型と参照型との違いは,値型の変数にはデータが直接格納されるのに対し,参照型の変数にはオブジ
ェクトへの参照が格納されるという点にある。参照型では,二つの変数から同じオブジェクトを参照でき
るため,ある変数の操作が,他の変数の参照しているオブジェクトにも影響する可能性がある。値型の場
合,各変数は,データの独自のコピーを保持しているため,ある変数の操作が他の変数に影響することは
ない。
次に例を示す。
using System;
struct Struct1
{
public int Value;
}
class Class1
{
public int Value = 0;
10
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
class Test
{
static void Main() {
Struct1 val1 = new Struct1();
Struct1 val2 = val1;
val2.Value = 123;
Class1 ref1 = new Class1();
Class1 ref2 = ref1;
ref2.Value = 123;
Console.WriteLine("Values: {0}, {1}", val1.Value,
val2.Value);
Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
}
}
この例は,値型と参照型との違いを示す。生成される出力は次となる。
Values: 0, 123
Refs: 123, 123
局所変数val2フィールドへの代入は,局所変数val1に影響を与えない。両方の局所変数が,値型(型
Struct1)であり,それぞれの変数が独自の記憶域をもつからである。対照的に,代入ref2.Value = 123;
は,参照ref1及びref2の双方に影響する。
次の2行について,もう少し説明する。
Console.WriteLine("Values: {0}, {1}", val1.Value, val2.Value);
Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
この2行は,可変個の実引数を受け取るConsole.WriteLineによる文字列の書式指定の振る舞いを
示す。第1引数は文字列で,{0}及び{1}のような番号付きのけた取り(placeholder)を含む。このけた取り
はその後に引き続く引数を参照しており,{0}は第2引数を,{1}は第3引数を参照する。出力が端末に
送信される前に,けた取り部分は,対応する実引数の書式指定された値に置き換えられる。
開発者は,列挙宣言及び構造体宣言を用いて新しい値型を定義したり,クラス宣言,インタフェース宣
言及び委譲宣言を用いて新しい参照型を定義したりできる。次に型宣言の例を示す。
using System;
public enum Color
{
Red, Blue, Green
}
public struct Point
{
public int x, y;
}
11
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public interface IBase
{
void F();
}
public interface IDerived: IBase
{
void G();
}
public class A
{
protected virtual void H() {
Console.WriteLine("A.H");
}
}
public class B: A, IDerived
{
public void F() {
Console.WriteLine("B.F, implementation of IDerived.F");
}
public void G() {
Console.WriteLine("B.G, implementation of IDerived.G");
}
override protected void H() {
Console.WriteLine("B.H, override of A.H");
}
}
public delegate void EmptyDelegate();
型宣言については,箇条16〜箇条25で詳しく示す。
8.2.1
あらかじめ定義された型
C#は,あらかじめ定義された型を提供する。そのほとんどは,C及びC++の開発者になじみがある。
あらかじめ定義された参照型は,object及びstringとする。型objectは,他のすべての型の基底
型となる。型stringは,Unicode文字列値を表すのに用いる。型stringの値は,変更不可である。
あらかじめ定義された値型は,符号付き及び符号なしの整数型,浮動小数点型,型bool,型char及
び型decimalを含む。符号付き整数型は,sbyte,short,int及びlongであり,符号なし整数型は,
byte,ushort,uint及びulongである。浮動小数点型は,float及びdoubleである。
型boolは,真又は偽を値とする真理値を表すのに用いる。boolを含めたことによって,文書化を兼
ねたコードを書くのが容易となり,開発者が“==”を使うべきなのに“=”を誤って使うというC++のあ
りふれたコーディングエラーを避けることができる。C#では,次のコードは正しくない。
int i = …;
F(i);
12
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
if (i = 0) // Bug: the test should be (i == 0)
G();
式i = 0がint型であり,if文にはbool型の式が必要なので,コンパイル時エラーになる。
型charは,Unicode符号単位を表すのに用いられる。char型の変数は,一つの16ビットUnicode符
号単位を表す。
型decimalは,浮動小数点表現によって丸め誤差が発生することを受け入れられない計算に適する。
一般的な例としては,税額の計算,通貨の変換などの財務計算がある。decimal型は,有効けた(桁)数
が少なくとも28とする。
次の表は,あらかじめ定義された型の一覧及び各型でリテラル値を記述する方法を示す。
型
説明
例
object
他のすべての型に対する最終的な基底型
object o = null;
string
文字列型,文字列は,Unicode 符号単位の列
string s = "hello";
sbyte
8 ビットの符号付き整数型
sbyte val = 12;
short
16 ビットの符号付き整数型
short val = 12;
int
32 ビットの符号付き整数型
int val = 12;
long
64 ビットの符号付き整数型
long val1 = 12;
long val2 = 34L;
byte
8 ビットの符号なし整数型
byte val1 = 12;
ushort
16 ビットの符号なし整数型
ushort val1 = 12;
uint
32 ビットの符号なし整数型
uint val1 = 12;
uint val2 = 34U;
ulong
64 ビットの符号なし整数型
ulong val1 = 12;
ulong val2 = 34U;
ulong val3 = 56L;
ulong val4 = 78UL;
float
単精度浮動小数点数型
float val = 1.23F;
double
倍精度浮動小数点数型
double val1 = 1.23;
double val2 = 4.56D;
bool
論理型,bool型の値は,真又は偽
bool val1 = true;
bool val2 = false;
char
文字型,char 型の値は,Unicode 符号単位
char val = 'h';
decimal
有効数字が少なくとも 28 けたの正確な 10進実数型
decimal val = 1.23M;
あらかじめ定義された型は,システムで提供される型の省略形となる。例えば,キーワードintは,構
造体System.Int32を参照する。作法として,完全なシステムの型名を使用するのではなく,キーワー
ドを使用するよう推奨する。
intなどのあらかじめ定義された値型に対しては特別な処理が行われることもあるが,ほとんどの場合
は,他の構造体と全く同じ処理が行われる。演算子の多重定義を使うと,あらかじめ定義された値型とほ
とんど同じ動作の新しい構造体型を定義できる。例えば,構造体Digitをあらかじめ定義された整数型と
同じ数学的演算を使えるよう定義して,Digitとあらかじめ定義された型との間の変換を定義できる。
あらかじめ定義された型自体も,演算子の多重定義を用いている。例えば,あらかじめ定義された異な
る型に対する比較演算子==及び!=は,次のように,それぞれ異なる意味をもつ。
− int型の二つの式が等価となるのは,同じ整数型値を表す場合である。
13
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− object型の二つの式が等価となるのは,同じオブジェクトを表す場合,又は双方がnullの場合で
ある。
− string型の二つの式が等価となるのは,文字列インスタンスが同じ長さをもち,各文字位置で同じ
文字をもつか,又は双方がnullの場合である。
次に例を示す。
using System;
class Test
{
static void Main() {
string s = "Test";
string t = string.Copy(s);
Console.WriteLine(s == t);
Console.WriteLine((object)s == (object)t);
}
}
この例は,次を出力する。
True
False
これは,最初の比較では,string型の二つの式を比較し,第2の比較では,object型の二つの式を
比較するからである。この例のSystem.WriteLineの場合のように,標準ライブラリが真理値の文字列
表現を生成するときには,“True”及び“False”を使うが,対応するC#言語の真理値リテラルは,true
及びfalseであることに注意する。
8.2.2
変換
あらかじめ定義された型は,あらかじめ定義された変換ももつ。例えば,あらかじめ定義された型int
とlongとの間には変換が存在する。C#は,暗黙の型変換と明示的型変換との2種類の変換を区別する。
暗黙の型変換は,注意して吟味しなくとも安全に行うことができる変換について与える。例えば,intか
らlongへの変換は暗黙の型変換である。この型変換は,常に成功し,情報は失われない。次に,intか
らlongへの暗黙の型変換の例を示す。
using System;
class Test
{
static void Main() {
int intValue = 123;
long longValue = intValue;
Console.WriteLine("{0}, {1}", intValue, longValue);
}
}
対照的に,明示的変換は,キャスト式によって行われる。次に,longからintへの明示的型変換を使
った例を示す。
using System;
14
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class Test
{
static void Main() {
long longValue = Int64.MaxValue;
int intValue = (int) longValue;
Console.WriteLine("(int) {0} = {1}", longValue, intValue);
}
}
出力はけた(桁)あふれが生じるので,次となる。
(int) 9223372036854775807 = -1
キャスト式は,暗黙の型変換にも明示的な型変換にも使用できる。
8.2.3
配列型
配列は,1次元又は多次元である。“長方形”配列及び“非長方形”配列の両方が使える。
1次元配列が,最も一般的である。次に例を示す。
using System;
class Test
{
static void Main() {
int[] arr = new int[5];
for(int i = 0; i < array.Length; i++) {
arr[i] = i * i;
for(int i = 0; i < array.Length; i++)
Console.WriteLine("arr[{0}] = {1}", i, arr[i]);
}
}
この例では,int型の値の1次元配列を生成し,配列要素を初期化してから出力する。生成される出力
は次となる。
arr[0] = 0
arr[1] = 1
arr[2] = 4
arr[3] = 9
arr[4] = 16
この例で使われる型int[]は,配列型である。配列型は,非配列型の後に一つ以上の位階指定子を付け
て記述する。次に例を示す。
class Test
{
static void Main() {
int[] a1;
// single-dimensional array of int
int[,] a2;
// 2-dimensional array of int
int[,,] a3;
// 3-dimensional array of int
15
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int[][] j2;
// "jagged" array: array of (array of
int)
int[][][] j3;
// array of (array of (array of int))
}
}
この例は,要素型としてint型をとる様々な配列型の局所変数宣言を示す。
配列型は参照型なので,配列変数宣言で確保されるのは,配列への参照のための領域だけである。配列
インスタンスは,実際には,配列初期化子及び配列生成式によって生成される。次に例を示す。
class Test
{
static void Main() {
int[] a1 = new int[] {1, 2, 3};
int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}};
int[,,] a3 = new int[10, 20, 30];
int[][] j2 = new int[3][];
j2[0] = new int[] {1, 2, 3};
j2[1] = new int[] {1, 2, 3, 4, 5, 6};
j2[2] = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
}
}
この例は,様々な配列生成式を示す。変数a1,a2及びa3は,長方形配列を表し,変数j2は,非長方
形配列を表す。この用語が,配列の形態に基づくことに驚かないほうがよい。長方形配列は,常に長方形
の形状となる。配列の各次元の長さから,長方形の形状は明らかである。例えば,a3の三つの次元の長さ
は,それぞれ10,20及び30となる。この配列が10*20*30個の要素を含むことは容易に分かる。
対照的に,変数j2は,“非長方形”配列,すなわち,“配列の配列”を表す。厳密にいえば,j2は,int
の配列の配列,又は,型int[]の1次元配列を表す。各int[]変数は,個別に初期化できるので,長方形
でない形態の配列を作成できる。この例は,int[]配列の各々に異なる長さを与える。具体的には,j2[0]
の長さは3,j2[1]の長さは6,j2[2]の長さは9である。
注記 C++では,int x[3][5][7]のように宣言された配列は,3次元長方形配列とみなされる。一
方,C#では,宣言int[][][]は,非長方形配列型を宣言する。
要素型並びに配列の形状(配列が非長方形型か長方形型かということ,及び位階の数)は,配列の型の
一部である。それに反し,(各次元の長さで表現される)配列の大きさは,配列の型の一部ではない。この
区別は,言語構文において明確に示される。すなわち,各次元の長さは,配列の型ではなく,配列生成式
の中で指定される。例えば,次の宣言では,配列の型はint[,,]であり,配列生成式はnew int[10, 20,
30]である。
int[,,] a3 = new int[10, 20, 30];
局所変数及びフィールドの宣言では,簡略形式を使用できるので,配列の型を指定し直す必要はない。
次に例を示す。
例
int[] a1 = new int[] {1, 2, 3};
16
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
この宣言は,次のように省略できる。プログラム上の意味は違わない。
例
int[] a1 = {1, 2, 3};
{1, 2, 3} のような配列初期化子の使われる文脈では,初期化する配列の型を規定する。次に例を示
す。
例
class Test
{
static void Main() {
short[] a = {1, 2, 3};
int[] b = {1, 2, 3};
long[] c = {1, 2, 3};
}
}
この例は,配列初期化子の同じ構文を異なる配列型に対して使用できることを示す。配列初期化子の型
を決定するには文脈が必要なため,配列の型を明示的に示さない式の文脈では,配列初期化子を使うこと
はできない。
8.2.4
型システム統合
C#は,“統合した型システム”を提供する。値型も含めてすべての型は,型objectから導出される。
intのような“基本的な”型の値に対してさえobjectのメソッドを呼び出すことが可能である。次に例
を示す。
例
using System;
class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}
この例は,objectで定義されているメソッドToStringを整数リテラルに対して呼び出し,結果とし
て出力“3”を得る。
次に,int型の値をobject型に変換して,再びint型に戻すことができるという更に興味深い例を示
す。
例
class Test
{
static void Main() {
int i = 123;
object o = i;
// boxing
int j = (int) o;
// unboxing
17
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
この例は,ボックス化及びボックス化解除の両方を示す。値型の変数を参照型に変換する必要がある場
合は,値を保持するためのオブジェクトの箱が割り当てられて,その箱に値がコピーされる。ボックス化
解除では,この逆の処理が行われる。オブジェクトの箱が元の値型にキャストされるときは,箱の値がコ
ピーされて,適切な記憶域に格納される。
この型システム統合によって,不必要な負荷を発生することなく,オブジェクトとしての利点が値型に
付加される。オブジェクトのような振る舞いをint値に必要としないプログラムでは,int値は,単に
32ビット値である。int値をオブジェクトのように扱う必要のあるプログラムでは,必要に応じて型シス
テム統合機能を利用できる。このように値型をオブジェクトとして扱うことができるため,ほとんどの言
語に存在する値型と参照型との間げき(隙)を埋めることができる。例えば,クラスStackは,object
値を取ったり返したりするPushメソッド及びPopメソッドを提供できる。
例
public class Stack
{
public object Pop() {…}
public void Push(object o) {…}
}
C#が統合した型システムをもつので,このクラスStackは,intのような値型も含めて任意の型の要
素に使うことができる。
8.3
変数及び仮引数
変数は,記憶域の場所を表す。すべての変数には型があり,型によって変数に格納できる値が決まる。
局所変数は,メソッド,特性又は添字子の中で宣言されている変数である。局所変数を定義するには,型
の名前,並びに変数名及び初期値からなる宣言子で指定する。宣言子の初期値は,省略してもよい。宣言
の例を次に示す。
例
int a;
int b = 1;
一つの局所変数宣言に,複数の宣言子を指定してよい。a及びbの宣言は,次のようにも書ける。
例
int a, b = 1;
変数から値を取得するには,先に変数に値を代入しておかなければならない。次に例を示す。
class Test
{
static void Main() {
int a;
int b = 1;
int c = a + b; // error, a not yet assigned
…
}
18
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
この例では,値が代入される前に,その変数a を使おうとしているため,コンパイル時エラーになる。
確実な代入に関する規則は,12.3で規定される。
フィールド(17.4参照)は,クラス若しくは構造体,又は,クラス若しくは構造体のインスタンスに関
連付けられた変数である。修飾子staticをもつフィールドは,静的変数を定義し,修飾子staticのな
いフィールドは,インスタンス変数を定義する。静的変数は,型と関連付けられ,インスタンス変数は,
インスタンスと関連付けられる。次に例を示す。
using Personnel.Data;
class Employee
{
private static DataSet ds;
public string Name;
public decimal Salary;
…
}
この例は,一つの非公開静的変数及び二つの公開インスタンス変数をもつクラスEmployeeを示す。
仮引数宣言でも,変数が定義される。仮引数には,値仮引数,参照仮引数,出力仮引数及び仮引数配列
の4種類がある。
値仮引数は,“入力 (in)”仮引数の引渡しに使われる。この場合は,実引数の値がメソッドに渡され,仮
引数を変更しても元の実引数には影響がない。値仮引数は,対応する実引数とは別の独自の変数を参照す
る。この変数は,対応する実引数の値をコピーすることで初期化される。次に例を示す。
using System;
class Test
{
static void F(int p) {
Console.WriteLine("p = {0}", p);
p++;
}
static void Main() {
int a = 1;
Console.WriteLine("pre: a = {0}", a);
F(a);
Console.WriteLine("post: a = {0}", a);
}
}
これは,pという名前の値仮引数をもつメソッドFを示す。値仮引数pが変更されているにもかかわら
ず,この例は,次を出力する。
pre: a = 1
p = 1
post: a = 1
19
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
参照仮引数は,“参照による”仮引数渡しのために用いられる。これは,仮引数が,呼出し側が提供した
実引数の別名として振る舞う。参照仮引数自体は,変数を定義せずに,対応する実引数の変数を参照する。
参照仮引数を変更すると,対応する実引数が影響を受ける。参照仮引数は,修飾子refによって宣言され
る。次に例を示す。
using System;
class Test
{
static void Swap(ref int a, ref int b) {
int t = a;
a = b;
b = t;
}
static void Main() {
int x = 1;
int y = 2;
Console.WriteLine("pre: x = {0}, y = {1}", x, y);
Swap(ref x, ref y);
Console.WriteLine("post: x = {0}, y = {1}", x, y);
}
}
これは,二つの参照仮引数をもつメソッドSwapを示す。生成される出力は,次となる。
pre: x = 1, y = 2
post: x = 2, y = 1
仮引数の宣言及びその使用の両方で,キーワードrefを使わなければならない。呼出し箇所でのref
の使用は,呼出しの結果として実引数の値が変わる可能性があることを,コードを読む開発者が理解する
よう注意を喚起する。
出力仮引数は,参照仮引数と似ているが,呼出し側が提供する実引数の初期値が重要ではない点で異な
る。出力仮引数は,修飾子outで宣言される。次に例を示す。
using System;
class Test
{
static void Divide(int a, int b, out int result, out int remainder)
{
result = a / b;
remainder = a % b;
}
static void Main() {
for (int i = 1; i < 10; i++)
for (int j = 1; j < 10; j++) {
20
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int ans, r;
Divide(i, j, out ans, out r);
Console.WriteLine("{0} / {1} = {2}r{3}", i, j, ans,
r);
}
}
}
この例のメソッドDivideでは,除算の商及び剰余として,二つの出力仮引数が使われる。
値仮引数,参照仮引数及び出力仮引数の場合,呼出し側が渡す実引数とそれを表現するために使われる
仮引数との間には,一対一の対応関係がある。仮引数配列の場合は,多対一の関係を使って,複数の実引
数を一つの仮引数配列で表現できる。言い換えれば,仮引数配列は,可変長実引数並びを実現する。
仮引数配列は,修飾子paramsで宣言される。一つのメソッドで使用できる仮引数配列は,一つだけで
あり,常に最後の仮引数として指定しなければならない。仮引数配列の型は,常に1次元配列型となる。
呼出し側は,この配列型の実引数を一つだけ渡すことも,この配列型の要素型の実引数を任意個数渡すこ
ともできる。次に例を示す。
using System;
class Test
{
static void F(params int[] args) {
Console.WriteLine("# of arguments: {0}", args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine("¥targs[{0}] = {1}", i, args[i]);
}
static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(new int[] {1, 2, 3, 4});
}
}
これは,可変個数のint引数を取るメソッドFと,このメソッドFの複数の呼出しとを示す。出力は次
となる。
# of arguments: 0
# of arguments: 1
args[0] = 1
# of arguments: 2
args[0] = 1
args[1] = 2
# of arguments: 3
21
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
args[0] = 1
args[1] = 2
args[2] = 3
# of arguments: 4
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4
箇条8の例の多くは,クラスConsoleのメソッドWriteLineを使う。次の例は,このメソッドの実
引数代入の振る舞いを示す。
int a = 1, b = 2;
Console.WriteLine("a = {0}, b = {1}", a, b);
この処理は,仮引数配列を使って実現される。WriteLineメソッドには,少数の実引数が渡される一
般的な状況に合わせて多重定義された幾つかのメソッド,及び仮引数配列を使用する一つのメソッドが用
意されている。
namespace System
{
public class Console
{
public static void WriteLine(string s) {…}
public static void WriteLine(string s, object a) {…}
public static void WriteLine(string s, object a, object b)
{…}
…
public static void WriteLine(string s, params object[] args)
{…}
}
}
8.4
自動メモリ管理
手動メモリ管理では,メモリブロックの割当て及び解放を開発者が管理する必要がある。手動メモリ管
理を利用した開発は,時間がかかるだけでなく技術的にも困難な場合がある。C#には自動メモリ管理機能
が用意されているため,開発者はこの煩雑な作業を行う必要はない。ほとんどの場合,自動メモリ管理機
能を利用することで,コードの品質及び開発者の生産性が向上し,表現能力及び性能が低下することはな
い。
次に例を示す。
using System;
public class Stack
{
private Node first = null;
public bool Empty {
22
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
get {
return (first == null);
}
}
public object Pop() {
if (first == null)
throw new Exception("Can't Pop from an empty Stack.");
else {
object temp = first.Value;
first = first.Next;
return temp;
}
}
public void Push(object o) {
first = new Node(o, first);
}
class Node
{
public Node Next;
public object Value;
public Node(object value): this(value, null) {}
public Node(object value, Node next) {
Next = next;
Value = value;
}
}
}
これは,Nodeインスタンスのリンク付き並びとして実装されたクラスStackを示す。Nodeインスタ
ンスは,Pushメソッドによって生成されて,必要なくなるとゴミ集めが行われる。Nodeのインスタンス
は,どのコードからももはやアクセスできなくなるとゴミ集めの候補となる。例えば,ある項目が Stack
から削除されると,それに関連したNodeインスタンスは,ゴミ集めの候補となる。
次に例を示す。
class Test
{
static void Main() {
Stack s = new Stack();
for (int i = 0; i < 10; i++)
s.Push(i);
s = null;
}
23
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
これは,クラスStackを使うコードを示す。Stackは,10個の要素で生成されて初期化され,値null
が代入される。変数sにいったんnullが代入されると,Stack及び関連した10個のNodeインスタン
スがゴミ集めの候補となる。ゴミ集め子は,すぐにゴミ集めを実行してかまわないのだが,すぐに実行す
る必要はない。
C#が基づいているゴミ集め子は,メモリ内でオブジェクトを移動するが,この動作は,ほとんどのC#
開発者からは不可視となる。一般的には自動メモリ管理に満足しているが,時々は,細粒度制御を必要と
したり,性能をもう少し上げる必要のある開発者に対しては,C#は,“安全でない”コードを書く機能を
提供する。そのようなコードでは,ポインタ型及びオブジェクトアドレスを直接扱うことができる。ただ
し,C#では,ゴミ集め子がそのオブジェクトを動かさないように,プログラマが一時的にオブジェクトを
固定する必要がある。
この“安全でない”コードの機能は,実際には,開発者及び利用者の観点からは“安全な”機能となる。
安全でないコードは,unsafe修飾子を使ってコードの中で明確に示されなければならず,開発者が安全
でない言語機能を誤って使用することはできず,コンパイラ及び実行エンジンが連携して,安全でないコ
ードがあたかも安全なコードであるかのように動作することを防ぐ。これらの制限によって,安全でない
コードは,コードが信頼できる状況においてだけ使用される。
次に例を示す。
using System;
class Test
{
static void WriteLocations(byte[] arr) {
unsafe {
fixed (byte* pArray = arr) {
byte* pElem = pArray;
for(int i = 0; i < array.Length; i++)
byte value = *pElem;
Console.WriteLine("arr[{0}] at 0x{1:X} is
{2}",
i, (uint)pElem, value);
pElem++;
}
}
}
}
static void Main() {
byte[] arr = new byte[] {1, 2, 3, 4, 5};
WriteLocations(arr);
}
}
この例で示されているWriteLocationsという名前のメソッドにある安全でないブロックは,配列の
24
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
インスタンスを固定し,ポインタ操作を使って要素に対する繰返し処理を行う。各配列要素の添字,値及
び位置が,端末に出力される。次に,出力の例を示す。
arr[0] at 0x8E0360 is 1
arr[1] at 0x8E0361 is 2
arr[2] at 0x8E0362 is 3
arr[3] at 0x8E0363 is 4
arr[4] at 0x8E0364 is 5
メモリ上での正確な位置は,アプリケーションを実行するたびに異なる可能性がある。
8.5
式
C#には,単項演算子,2項演算子及び3項演算子がある。次の表は,演算子を優先順位の高いものから
低いものへとまとめている。
節
種類
演算子
14.5
一次式
x.y f(x) a[x] x++ x-- new
typeof checked unchecked
14.6
単項式
+ - ! ~ ++x --x (T)x
14.7
乗除式
* / %
14.7
加減式
+ -
14.8
シフト
<< >>
14.9
関係式及び型検査
< > <= >= is as
14.9
等価性
== !=
14.10
論理 AND
&
14.10
論理 XOR
^
14.10
論理 OR
|
14.11
条件 AND
&&
14.11
条件 OR
||
14.13
二択条件式
?:
14.14
代入演算子
= *= /= %= += -= <<= >>= &= ^= |=
一つの式に複数の演算子が含まれていると,演算子の優先順位によって,演算子が評価される順序が制
御される。例えば,式x + y * zは,x + (y * z)として評価される。なぜならば,演算子*が,演算
子+より高い優先順位をもつためである。
同じ優先順位の二つの演算子の間に演算対象がある場合は,演算子の結合規則によって,演算の実行順
序が制御される。
− 代入演算子を除くすべての2項演算子の結合規則は,左結合である。すなわち,演算は,左から右に
実行される。例えば,x + y + z は,(x + y) + zとして評価される。
− 代入演算子及び二択条件演算子(?:)の結合規則は,右結合である。すなわち,演算は,右から左に
実行される。例えば,x = y = zは,x = (y = z)として評価される。
優先順位及び結合規則は,括弧を使って制御できる。例えば,x + y * zは,最初に,yにzを乗じ,
次に,その結果にxを加える。しかし,(x + y) * zは,最初に,xにyを加え,次に,その結果にz
を乗算する。
8.6
文
C#の文の大部分は,C及びC++から流用されているが,注目に値する追加機能及び変更点が幾つかある。
25
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
次の表は,使用できる文の種類及びそれぞれについての例を示す。
文
例
文並び及びブロック文
static void Main() {
F();
G();
{
H();
I();
}
}
ラベル付き文及びgoto
文
static void Main(string[] args) {
if (args.Length == 0)
goto done;
Console.WriteLine(args.Length);
done:
Console.WriteLine("Done");
}
局所定数宣言
static void Main() {
const float pi = 3.14f;
const int r = 123;
Console.WriteLine(pi * r * r);
}
局所変数宣言
static void Main() {
int a;
int b = 2, c = 3;
a = 1;
Console.WriteLine(a + b + c);
}
式文
static int F(int a, int b) {
return a + b;
}
static void Main() {
F(1, 2); // Expression statement
}
if文
static void Main(string[] args) {
if (args.Length == 0)
Console.WriteLine("No args");
else
Console.WriteLine("Args");
}
switch文
static void Main(string[] args) {
switch (args.Length) {
case 0:
Console.WriteLine("No args");
break;
case 1:
Console.WriteLine("One arg ");
break;
default:
int n = args.Length;
Console.WriteLine("{0} args", n);
break;
}
}
26
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
文
例
while文
static void Main(string[] args) {
int i = 0;
while (i < args.Length) {
Console.WriteLine(args[i]);
i++;
}
}
do文
static void Main() {
string s;
do { s = Console.ReadLine(); }
while (s != "Exit");
}
for文
static void Main(string[] args) {
for (int i = 0; i < args.Length; i++)
Console.WriteLine(args[i]);
}
foreach文
static void Main(string[] args) {
foreach (string s in args)
Console.WriteLine(s);
}
break文
static void Main(string[] args) {
int i = 0;
while (true) {
if (i == args.Length)
break;
Console.WriteLine(args[i++]);
}
}
continue文
static void Main(string[] args) {
int i = 0;
while (true) {
Console.WriteLine(args[i++]);
if (i < args.Length)
continue;
break;
}
}
return文
static int F(int a, int b) {
return a + b;
}
static void Main() {
Console.WriteLine(F(1, 2));
return;
}
yield 文
static IEnumerable<int> FromTo(int a, int b) {
if (a > b)
yield break;
for ( ; ; a++) {
yield return a;
if (a == b)
break;
}
}
27
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
文
例
throw文及びtry文
static int F(int a, int b) {
if (b == 0)
throw new Exception("Divide by zero");
return a / b;
}
static void Main() {
try {
Console.WriteLine(F(5, 0));
}
catch(Exception e) {
Console.WriteLine("Error");
}
}
checked文及び
unchecked文
static void Main() {
int x = Int32.MaxValue;
Console.WriteLine(x + 1); // Overflow
checked {
Console.WriteLine(x + 1); // Exception
}
unchecked {
Console.WriteLine(x + 1); // Overflow
}
}
lock文
static void Main() {
A a = …;
lock(a) {
a.P = a.P + 1;
}
}
using文
static void Main() {
using (Resource r = new Resource()) {
r.F();
}
}
8.7
クラス
クラスを宣言すると,新しい参照型が定義される。クラスは,他のクラスから継承でき,インタフェー
スを実装できる。総称クラス宣言(25.1参照)は,一つ以上の型仮引数をとる。
クラスのメンバには,定数,フィールド,メソッド,特性,イベント,添字子,演算子,インスタンス
構築子,終了化子,静的構築子及び入れ子型宣言を含むことができる。各メンバには,そのメンバにアク
セス可能なプログラムテキストの領域を制御するアクセス可能性(10.5参照)が付随する。アクセス可能
性には,五つの可能な形式がある。次の表は,アクセス可能性をまとめる。
形式
概要
public
アクセスは制限されない。
protected
アクセスは,包含するクラス,又は包含するクラスから派生した型に制限される。
internal
アクセスは,このプログラムに制限される。
protected internal アクセスは,このプログラム,又は包含するクラスから派生した型に制限される。
private
アクセスは,包含する型に制限される。
次に例を示す。
28
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class MyClass
{
public const int MyConst = 12;
public int MyField = 34;
public static int MyStaticField = 34;
public void MyMethod(){
Console.WriteLine("MyClass.MyMethod");
}
public int MyProperty {
get {
return MyField;
}
set {
MyField = value;
}
}
public event EventHandler MyEvent;
public int this[int index] {
get {
return 0;
}
set {
Console.WriteLine("this[{0}] = {1}", index, value);
}
}
public static MyClass operator+(MyClass a, MyClass b) {
return new MyClass(a.MyField + b.MyField);
}
public MyClass() {
Console.WriteLine("Instance constructor");
}
public MyClass(int value) {
MyField = value;
Console.WriteLine("Instance constructor");
}
~MyClass() {
Console.WriteLine("Finalizer");
}
static MyClass() {
29
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
MyStaticField *= 2;
Console.WriteLine("Static constructor");
}
internal class MyNestedClass
{}
}
この例は,各種のメンバを含むクラスを示した。次の例は,メンバの使い方を示す。
class Test
{
static void Main() {
// Instance constructor usage
MyClass a = new MyClass();
MyClass b = new MyClass(123);
// Constant usage
Console.WriteLine("MyConst = {0}", MyClass.MyConst);
// Field usage
a.MyField++;
Console.WriteLine("a.MyField = {0}", a.MyField);
// Method usage
a.MyMethod();
// Property usage
a.MyProperty++;
Console.WriteLine("a.MyProperty = {0}", a.MyProperty);
// Indexer usage
a[3] = a[1] = a[2];
Console.WriteLine("a[3] = {0}", a[3]);
// Event usage
a.MyEvent += new EventHandler(MyHandler);
// Overloaded operator usage
MyClass c = a + b;
// Nested type usage
MyClass.MyNestedClass d = new MyClass.MyNestedClass();
}
static void MyHandler(object sender, EventArgs e) {
Console.WriteLine("Test.MyHandler");
}
}
8.7.1
定数
定数は,コンパイル時に算出できる定数値を表現するクラスメンバである。循環的な依存関係にならな
い限り,定数は同じプログラム内の他の定数に依存できる。定数式に関する規則は,14.16で規定する。次
30
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
の例は,二つの公開定数をもつConstantsという名前のクラスを示す。
class Constants
{
public const int A = 1;
public const int B = A + 1;
}
定数は,静的メンバであると考えられるにもかかわらず,定数宣言には,修飾子staticは,必要とも
しないし,許されてもいない。次に示すように,クラスを用いて定数にアクセスできる。
using System;
class Test
{
static void Main() {
Console.WriteLine("{0}, {1}", Constants.A, Constants.B);
}
}
これは,Constants.A及びConstants.Bの値をそれぞれ出力する。
8.7.2
フィールド
フィールドは,オブジェクト又はクラスに関連付けられた変数を表現するメンバである。次に例を示す。
class Color
{
internal ushort redPart;
internal ushort bluePart;
internal ushort greenPart;
public Color(ushort red, ushort blue, ushort green) {
redPart = red;
bluePart = blue;
greenPart = green;
}
public static Color Red = new Color(0xFF, 0, 0);
public static Color Blue = new Color(0, 0xFF, 0);
public static Color Green = new Color(0, 0, 0xFF);
public static Color White = new Color(0xFF, 0xFF, 0xFF);
}
ここで示すクラスColorは,redPart,bluePart及びgreenPart,という名前の内部インスタンス
フィールド並びにRed,Blue,Green及びWhiteという名前の静的フィールドをもつ。
静的フィールドをこのように使用することは,望ましくない。フィールドは,使用される前のどこかの
時点で初期化されるが,この初期化以降に,クライアントがフィールドを変更することを阻止できない。
そのような変更は,Colorを使っていて,その値が変化しないと思いこんでいる他のプログラムに予測で
きないエラーをもたらす可能性がある。読込み専用フィールドは,そのような問題を防止するために使わ
れる。読込み専用フィールドへの代入は,宣言の一部としてだけ,又は同一クラスのインスタンス構築子
31
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
及び静的構築子の中だけで行える。静的読込み専用フィールドは,静的構築子の中で代入可能であり,非
静的読込み専用フィールドは,インスタンス構築子の中で代入可能である。したがって,クラスColor
は,次のように,静的フィールドに修飾子readonlyを加えることによって強化できる。
class Color
{
internal ushort redPart;
internal ushort bluePart;
internal ushort greenPart;
public Color(ushort red, ushort blue, ushort green) {
redPart = red;
bluePart = blue;
greenPart = green;
}
public static readonly Color Red = new Color(0xFF, 0, 0);
public static readonly Color Blue = new Color(0, 0xFF, 0);
public static readonly Color Green = new Color(0, 0, 0xFF);
public static readonly Color White = new Color(0xFF, 0xFF, 0xFF);
}
8.7.3
メソッド
メソッドは,オブジェクト又はクラスによって実行される計算又は動作を実装するメンバである。メソ
ッドは,空も可能な仮引数並び,及び(返却値の型がvoid以外の場合は)返却値をもち,静的又は非静
的のいずれかとなる。静的メソッドは,クラスを通じてアクセスされる。非静的メソッドは,インスタン
スメソッドとも呼ばれ,クラスのインスタンスを通じてアクセスされる。総称メソッド(25.6参照)は,
型仮引数の並びをもつ。次に例を示す。
using System;
public class Stack
{
public static Stack Clone(Stack s) {…}
public static Stack Flip(Stack s) {…}
public object Pop() {…}
public void Push(object o) {…}
public void PushMultiple<T>(T[] a) {…}
public override string ToString() {…}
…
}
class Test
{
static void Main() {
Stack s = new Stack();
for (int i = 1; i < 10; i++)
32
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
s.Push(i);
Stack flipped = Stack.Flip(s);
Stack cloned = Stack.Clone(s);
Console.WriteLine("Original stack: " + s.ToString());
Console.WriteLine("Flipped stack: " + flipped.ToString());
Console.WriteLine("Cloned stack: " + cloned.ToString());
}
}
これは,幾つかの静的メソッド(Clone及びFlip),幾つかのインスタンスメソッド(Pop,Push及
びToString)及び総称メソッド(PushMultiple<T>)をもつクラスStackを示す。
メソッドは,多重定義してよいが,これは,複数のメソッドが,一意な呼出し情報をもつ限りは,同一
の名前をもってよいことを意味する。メソッドの呼出し情報は,メソッドの名前と,仮引数の個数,修飾
子及び型,並びに総称型仮引数の個数とで構成される。メソッドの呼出し情報には,返却値の型も,仮引
数又は型仮引数の名前も含まれない。次に,F と呼ばれる多数のメソッドをもつクラスの例を示す。
using System;
class Test
{
static void F() {
Console.WriteLine("F()");
}
static void F(object o) {
Console.WriteLine("F(object)");
}
static void F(int value) {
Console.WriteLine("F(int)");
}
static void F(ref int value) {
Console.WriteLine("F(ref int)");
}
static void F(int a, int b) {
Console.WriteLine("F(int, int)");
}
static void F(int[] values) {
Console.WriteLine("F(int[])");
}
static void F<T>(T t) {
Console.WriteLine("F<T>(T)");
}
static void Main() {
F();
33
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
F(1);
int i = 10;
F(ref i);
F((object)1);
F(1, 2);
F(new int[] {1, 2, 3});
F("Hello");
F<string>("World");
}
}
この生成する出力は,次となる。
F()
F(int)
F(ref int)
F(object)
F(int, int)
F(int[])
F<T>(T)
F<T>(T)
8.7.4
特性
特性は,オブジェクト又はクラスの特質へのアクセスを提供するメンバである。特性の例としては,文
字列の長さ,フォントの大きさ,ウィンドウの説明文,顧客の名前などがある。特性は,フィールドを自
然に拡張したものと考えられる。いずれも型をもつ名前付きのメンバであり,アクセスするための構文も
同じである。ただし,特性は,フィールドとは異なり,記憶域を示さない。代わりに,特性には,値の読
込み時又は書出し時に実行される文を指定するアクセス子がある。
特性は,特性宣言で定義される。特性宣言の最初の部分は,フィールド宣言とよく似ている。第2の部
分は,getアクセス子及び/又はsetアクセス子を含む。次の例では,クラスButtonが,特性Caption
を定義する。
public class Button
{
private string caption;
public string Caption {
get {
return caption;
}
set {
caption = value;
Repaint();
}
}
34
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
…
}
Captionのような,読込み及び書出しの両方が可能な特性は,getアクセス子及びsetアクセス子の
両方を含む。特性の値を読み込むときはgetアクセス子が呼び出され,特性の値を書き出すときはset
アクセス子が呼び出される。setアクセス子では,valueという名前の暗黙の仮引数によって,特性の新
しい値が引き渡される。
特性の宣言は比較的簡単だが,特性の実際の価値は,使ってみて初めて分かる。例えば,特性Caption
は,フィールドの読込み及び書出しと同じように,読込み及び書出しができる。
Button b = new Button();
b.Caption = "ABC";
// set; causes repaint
string s = b.Caption;
// get
b.Caption += "DEF";
// get & set; causes repaint
8.7.5
イベント
イベントは,オブジェクト及びクラスが通知を発行できるようにするためのメンバである。クラスは,
(キーワードeventを追加することを除いては,フィールド宣言と同じ)イベント宣言及び省略可能なイ
ベントアクセス子を与えることによってイベントを定義する。イベント宣言での型は,委譲型でなければ
ならない。
委譲型のインスタンスは,呼出し可能な実体を一つ以上カプセル化する。(委譲生成にかかわるのが)イ
ンスタンスメソッドの場合,呼出し可能な実体は,インスタンス及びそのインスタンスのメソッドで構成
される。静的メソッドの場合,呼出し可能な実体は,メソッドだけで構成される。委譲インスタンス及び
適切な実引数並びが与えられると,その実引数並びで委譲インスタンスのすべてのメソッドを呼び出すこ
とができる。
次に,クラスButtonが型EventHandlerのイベントClickを定義する例を示す。
public delegate void EventHandler(object sender, System.EventArgs e);
public class Button
{
public event EventHandler Click;
public void Reset() {
Click = null;
}
}
クラスButtonの内側では,メンバClickは,あたかも型EventHandlerの非公開フィールドと全く
同じとなる。ただし,クラスButtonの外側では,メンバClickは,演算子+=及び‒=の左側でしか使え
ない。演算子+=は,イベントのためのハンドラを追加し,演算子-=は,イベントのためのハンドラを取り
除く。次に,Button1のイベントClick用のイベントハンドラとしてButton1̲Clickを追加したクラ
スForm1の例を示す。
using System;
public class Form1
{
public Form1() {
35
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
// Add Button1̲Click as an event handler for Button1ʼs Click
event
Button1.Click += new EventHandler(Button1̲Click);
}
Button Button1 = new Button();
void Button1̲Click(object sender, EventArgs e) {
Console.WriteLine("Button1 was clicked!");
}
public void Disconnect() {
Button1.Click -= new EventHandler(Button1̲Click);
}
}
メソッドDisconnectでは,このイベントハンドラが取り除かれる。
次のような簡単なイベント宣言に対しては,コンパイラが,+=演算子及び-=演算子の基になる実装を自
動的に作成する。
public event EventHandler Click;
更にきめ細かい制御が必要な場合は,addアクセス子及びremoveアクセス子を明示的に作成すること
で実現できる。例えば,クラスButtonは,次のように書き直せる。
public class Button
{
private EventHandler handler;
public event EventHandler Click {
add { handler += value; }
remove { handler -= value; }
}
}
このように変更してもクライアントのコードに影響はなくて,クラスButtonの実装の柔軟性が増す。
例えば,Clickのためのイベントハンドラは,フィールドによって表現される必要がない。
8.7.6
演算子
演算子は,クラスのインスタンスに適用できる式演算子の意味を定義するメンバである。単項演算子,2
項演算子及び変換演算子という3種類の演算子を定義できる。
次の例は,0〜9の整数値である10進数字を表現する型Digitを定義する。
using System;
public struct Digit
{
byte value;
public Digit(int value) {
if (value < 0 || value > 9) throw new ArgumentException();
this.value = (byte)value;
36
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
public static implicit operator byte(Digit d) {
return d.value;
}
public static explicit operator Digit(int value) {
return new Digit(value);
}
public static Digit operator+(Digit a, Digit b) {
return new Digit(a.value + b.value);
}
public static Digit operator-(Digit a, Digit b) {
return new Digit(a.value - b.value);
}
public static bool operator==(Digit a, Digit b) {
return a.value == b.value;
}
public static bool operator!=(Digit a, Digit b) {
return a.value != b.value;
}
public override bool Equals(object value) {
if (value == null) return false;
if (GetType() == value.GetType()) return this == (Digit)value;
return false;
}
public override int GetHashCode() {
return value.GetHashCode();
}
public override string ToString() {
return value.ToString();
}
}
class Test
{
static void Main() {
Digit a = (Digit) 5;
Digit b = (Digit) 3;
Digit plus = a + b;
Digit minus = a - b;
bool equals = (a == b);
Console.WriteLine("{0} + {1} = {2}", a, b, plus);
37
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine("{0} - {1} = {2}", a, b, minus);
Console.WriteLine("{0} == {1} = {2}", a, b, equals);
}
}
型Digitは,次の演算子を定義する。
− Digitからbyteへの暗黙の型変換演算子。
− intからDigitへの明示的型変換演算子。
− 二つのDigit値を加えて一つのDigit値を返す加算演算子。
− 一つのDigit値からもう一つを引き去り,一つのDigit値を返す減算演算子。
− 二つのDigit値を比較する等号(==)及び不等号(!=)演算子。
8.7.7
添字子
添字子は,配列と同じようにオブジェクトに添字指定することを可能にするメンバである。特性を使う
とフィールドに似た方法でアクセスできるのと同様に,添字子を使うと配列に似た方法でアクセスできる。
例えば,8.7.3で例示したクラスStackを考える。このクラスを設計する場合,不必要なPush及びPop
の操作を実行せずに,スタックの項目を調べたり変更したりできるように,配列と同様のアクセス手段を
開示する場合がある。これによって,クラスStackはリンクされた並びとして実装されていても,配列の
ように簡単にアクセスすることもできる。
添字子宣言は,特性宣言と似ているが,主たる相違は,添字子が無名であり(thisが添字付けられる
ので,宣言で用いられる“名前”は,thisになる。),添字指定仮引数を含むことにある。添字指定仮引
数は,角括弧で囲んで指定する。次に,クラスStackの添字子の例を示す。
using System;
public class Stack
{
private Node GetNode(int index) {
Node temp = first;
while (true) {
if (temp == null || index < 0)
throw new Exception("Index out of range.");
if (index == 0)
return temp;
temp = temp.Next;
index--;
}
}
public object this[int index] {
get {
return GetNode(index).Value;
}
set {
GetNode(index).Value = value;
38
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
…
}
class Test
{
static void Main() {
Stack s = new Stack();
s.Push(1);
s.Push(2);
s.Push(3);
s[0] = 33;
// Changes the top item from 3 to 33
s[1] = 22;
// Changes the middle item from 2 to 22
s[2] = 11;
// Changes the bottom item from 1 to 11
}
}
8.7.8
インスタンス構築子
インスタンス構築子は,クラスのインスタンスを初期化するために必要な動作を実装するメンバである。
次に,二つの公開インスタンス構築子を提供するクラスPointの例を示す。そのうちの一つは,引数を
取らないが,もう一つは,二つのdouble引数を取る。
using System;
class Point
{
public double x, y;
public Point() {
this.x = 0;
this.y = 0;
}
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public static double Distance(Point a, Point b) {
double xdiff = a.x - b.x;
double ydiff = a.y - b.y;
return Math.Sqrt(xdiff * xdiff + ydiff * ydiff);
}
public override string ToString() {
return string.Format("({0}, {1})", x, y);
}
39
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
class Test
{
static void Main() {
Point a = new Point();
Point b = new Point(3, 4);
double d = Point.Distance(a, b);
Console.WriteLine("Distance from {0} to {1} is {2}", a, b,
d);
}
}
クラスに対してインスタンス構築子を定義しない場合,直接基底クラスの仮引数のないインスタンス構
築子を呼び出すだけの仮引数をもたないインスタンス構築子が自動的に生成される。
8.7.9
終了化子
終了化子は,クラスのインスタンスを終了化するために必要な動作を実装するメンバである。終了化子
は,仮引数をもつことも,アクセス可能性修飾子を指定することもできない。また,明示的に呼び出すこ
ともできない。インスタンスの終了化子は,ゴミ集めの最中に自動的に呼び出される。
次に,終了化子をもつクラスPointの例を示す。
using System;
class Point
{
public double x, y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
~Point() {
Console.WriteLine("Finalized {0}", this);
}
public override string ToString() {
return string.Format("({0}, {1})", x, y);
}
}
8.7.10 静的構築子
静的構築子は,クラスを初期化するために必要な動作を実装するメンバである。静的構築子は,仮引数
をもつことも,アクセス可能性修飾子を指定することもできない。また,明示的に呼び出すこともできな
い。クラスの静的構築子は,自動的に呼び出される。
次に,静的フィールドを初期化する静的構築子をもつクラスEmployeeの例を示す。
using Personnel.Data;
class Employee
40
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
private static DataSet ds;
static Employee() {
ds = new DataSet(…);
}
public string Name;
public decimal Salary;
…
}
8.7.11 継承
クラスでは単一継承を使い,型objectがすべてのクラスのための最終的な基底クラスとなる。
これまでに示した例は,すべて暗黙にobjectから導出される。次に,objectから暗黙に導出される
クラスAの例を示す。
using System;
class A
{
public void F() { Console.WriteLine("A.F"); }
}
次に,Aから導出されるクラスBの例を示す。
class B: A
{
public void G() { Console.WriteLine("B.G"); }
}
class Test
{
static void Main() {
B b = new B();
b.F();
// Inherited from A
b.G();
// Introduced in B
A a = b;
// Treat a B as an A
a.F();
}
}
クラスBは,AのメソッドFを継承し,それ自身のメソッドGを導入する。
メソッド,特性及び添字子は,仮想にし,その実装を派生クラスで上書きできる。次に,仮想メソッド
FをもつクラスA,及びFを上書きするクラスBの例を示す。
using System;
class A
{
41
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public virtual void F() { Console.WriteLine("A.F"); }
}
class B: A
{
public override void F() {
base.F();
Console.WriteLine("B.F");
}
}
class Test
{
static void Main() {
B b = new B();
b.F();
A a = b;
a.F();
}
}
Bにおける上書きメソッドは,呼出しbase.F()を含む。これは,Aにおける上書き前のメソッドFを
呼び出す。
クラスに対してabstract修飾子を使用することによって,そのクラスは不完全であって,別のクラス
の基底クラスとして使用することだけを目的としていることを表せる。そのようなクラスは,抽象クラス
と呼ばれる。抽象クラスでは,抽象メンバを指定できる。非抽象派生クラスでは,抽象メンバを実装する
必要がある。次に,抽象クラスAにおいて抽象メソッドFを導入する例を示す。
using System;
abstract class A
{
public abstract void F();
}
class B: A
{
public override void F() { Console.WriteLine("B.F"); }
}
class Test
{
static void Main() {
B b = new B();
b.F();
A a = b;
a.F();
42
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
非抽象クラスBは,このメソッドの実装を提供する。
8.7.12 静的クラス
インスタンス化を意図せず,静的メソッドだけを含むクラスは,静的クラスとして宣言するのが望まし
い。そのようなクラスの例には,System.Console 及びSystem.Environmentがある。静的クラスは,
暗黙に封印されており,インスタンス構築子をもたない。静的クラスは,演算子typeof及びクラス要素
へのアクセスだけに用いられる。特に,静的クラスを変数の型又は型実引数として用いることはできない。
public static class Months
{
static Months() { … }
private static readonly string[] monthName = { … }
public static string GetMonthName(int mm) { … }
private static readonly int[,] daysInMonth = { … }
public static int GetDaysInMonth(bool isLeapYear, int mm) { … }
public static bool IsLeapYear(int yy) { … }
}
8.7.13 部分型宣言
状況によっては,型の宣言が非常に大きくなってしまい,単一のソースファイルに格納することが実用
的でなくなることがある。そのような場合には,そのクラス宣言を複数のソースファイルに分割し,それ
ぞれを比較的独立したものにするのが望ましいことが多い。
よく見受けられる状況としては,コードが一人の人物ではなく,プログラムから生成された場合がある。
フレームワークに富んだ開発環境においては,ビジュアルフォーム設計,データベーススキーマ,RPC記
述などから自動的に生成されたプロジェクトのソースコードを保持しておくことが効率的な場合がよくあ
る。この種のツールは,生産性を高めるが,生成されたクラスにメンバを追加するなどで,その出力をカ
スタム化しようとすると,多くの問題に悩まされることとなる。コード生成器の出力を直接変更した場合,
そのような変更は,コード生成器が再度用いられたときに失われてしまう。カスタム化した追加部分を別
のソースファイルに貯えておけば,修正が失われることを大幅に減少したり,失われないようにできる。
このような場合に,部分型宣言は,クラス,構造体又はインタフェースの定義を必要なだけ多くの異な
る部分に分割することによって,大幅な柔軟性を可能にする。例えば,次のようなソースファイルを一緒
にコンパイルすることが可能となる。
// machine-generated code in file #1
partial class Widget
{
private int[] counts;
public string ToString() {
// ...
}
}
// programmer-generated code in file #2
43
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
partial class Widget
{
private int value;
private void Helper() {
// ...
}
public int Process(object obj) {
// ...
}
}
ここで,Widgetのメンバは,それぞれのファイルで定義されているすべてのメンバの集合和となる。
8.8
構造体
クラスと構造体との間には,多くの類似点がある。構造体は,インタフェースを実装でき,クラスと同
じ種類のメンバをもつことができる。ただし,構造体とクラスとの間には,幾つか重要な違いがある。構
造体は,参照型ではなく値型であり,継承ができない。構造体の値は,“スタック上”すなわち“インライ
ン”に蓄えられる。構造体を上手に使用することで,性能が向上する場合がある。
例えば,Pointをクラスとしてではなく構造体として使用すると,実行時のメモリ割当ての回数に大き
な差が発生する可能性がある。次のプログラムでは,100個のPointの配列を生成し,初期化する。クラ
スとして実装されたPointでは,101個の独立したオブジェクト(配列に対して一つ,及び100個の各要
素に対して一つずつ)が具現化される。
class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++)
points[i] = new Point(i, i*i);
}
}
Pointが次のように構造体として実装されると,配列に対する一つのオブジェクトだけが具現化される。
struct Point
{
public int x, y;
44
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
この例では,インスタンスPointは,配列内にインラインで割り当てられる。このような最適化は,誤
って使用される場合もある。クラスの代わりに構造体を使用すると,アプリケーションの実行速度が遅く
なったり,メモリの使用量が増えたりする可能性もある。これは,構造体のインスタンスを値で渡すと,
構造体のコピーが生成されるためである。
8.9
インタフェース
インタフェースは,契約を定義する。インタフェースを実装するクラス及び構造体は,その契約に合致
する必要がある。インタフェースには,メソッド,特性,イベント及び添字子を(公開)メンバとして含
められる。
次に,添字子,イベントE,メソッドF及び特性Pを含むインタフェースの例を示す。
interface IExample
{
string this[int index] { get; set; }
event EventHandler E;
void F(int value);
string P { get; set; }
}
public delegate void EventHandler(object sender, EventArgs e);
インタフェースでは,多重継承が可能である。次に,ITextBox及びIListBoxの両方から継承したイ
ンタフェースIComboBoxの例を示す。
interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
interface IListBox: IControl
{
void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}
クラス及び構造体は,複数のインタフェースを実装できる。次に,クラスControlから派生され,
IControl及びIDataBoundの両方を実装するクラスEditBoxの例を示す。
interface IDataBound
45
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
void Bind(Binder b);
}
public class EditBox: Control, IControl, IDataBound
{
public void Paint() {…}
public void Bind(Binder b) {…}
}
この例では,インタフェースIControlからのメソッドPaint及びインタフェースIDataBoundから
のメソッドBindがクラスEditBoxの公開メンバを使って実装される。C#には,このようなメソッドを
実装する別の方法があり,メンバを公開にせずにクラスを実装できる。すなわち,限定名を使って,イン
タフェースのメンバを実装できる。例えば,クラスEditBoxは,メソッドIControl.Paint及び
IDataBound.Bindを提供することによって実装できる。
public class EditBox: IControl, IDataBound
{
void IControl.Paint() {…}
void IDataBound.Bind(Binder b) {…}
}
実装されるインタフェースメンバを各メンバが明示的に指し示すので,この方法で実装されるインタフ
ェースメンバのことを明示的インタフェースメンバと呼ぶ。明示的インタフェースメンバは,インタフェ
ースを用いてだけ呼び出すことができる。例えば,EditBoxによるメソッドPaintの実装は,インタフ
ェースIControlを通じてだけ呼出し可能である。
class Test
{
static void Main() {
EditBox editbox = new EditBox();
editbox.Paint();
// error: no such method
IControl control = editbox;
control.Paint();
// calls EditBoxʼs Paint
implementation
}
}
8.10 委譲
委譲は,他の言語では関数ポインタと呼ばれる処理方式を可能にする。ただし,委譲は,関数ポインタ
と異なり,オブジェクト指向及び型安全という特徴がある。
委譲宣言は,クラスSystem.Delegateから派生されたクラスを定義する。委譲インスタンスは,呼出
し可能実体として参照される一つ以上のメソッドをカプセル化する。インスタンスメソッドの場合,呼出
し可能な実体は,インスタンス及びそのインスタンスのメソッドで構成される。静的メソッドの場合,呼
出し可能な実体は,メソッドだけで構成される。委譲インスタンス及び適切な実引数並びが与えられると,
その実引数並びでその委譲インスタンスのすべてのメソッドを呼び出すことができる。
46
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
委譲インスタンスの興味深く有用な性質としては,委譲インスタンスがカプセル化しているメソッドの
クラスについては,何も知らず,関心もないということがある。重要なのは,これらのメソッドと委譲の
型との間に一貫性がある(22.1参照)ことだけである。これによって,委譲は,“無名”呼出し用に完全に
適切なものとなる。これは強力な能力である。
委譲を定義して使用するには,宣言,具現化及び呼出しの三つの手順がある。委譲は,委譲宣言構文を
用いて宣言される。次に,引数を取らず何の結果も返さないSimpleDelegateという名前の委譲を宣言
する例を示す。
delegate void SimpleDelegate();
次に,インスタンスSimpleDelegateを生成して,直ちにそれを呼び出す例を示す。
class Test
{
static void F() {
System.Console.WriteLine("Test.F");
}
static void Main() {
SimpleDelegate d = new SimpleDelegate(F);
d();
}
}
この例は,メソッドに対する委譲を具現し,すぐに委譲を用いてメソッドを呼び出すが,このような使
用法は,あまり意味がない。メソッドを直接呼び出すほうが簡単である。委譲の真の有用性が分かるのは,
委譲の無名性を使う場合である。次に,SimpleDelegateを繰り返し呼び出すメソッドMultiCallの
例を示す。
void MultiCall(SimpleDelegate d, int count) {
for (int i = 0; i < count; i++) {
d();
}
}
メソッドMultiCallは,SimpleDelegateの目標メソッドの型,それがどのようなアクセス可能性
をもつか,又は,それが静的かどうかを知りもせず,気にも掛けない。問題になるのは,目標メソッドが
SimpleDelegateと一貫性があるかどうかだけである(22.1参照)。
8.11 enum
列挙 (enum) 型の宣言では,関連する記号定数群に対する型名を定義する。enumは,“複数の選択肢”
が存在する場合に使用する。このとき,実行時の決定は,コンパイル時に明らかになる固定個数の選択肢
から行われる。
次に,enum Color及びこのenumを用いるメソッドの例を示す。
enum Color
{
Red,
Blue,
47
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Green
}
class Shape
{
public void Fill(Color color) {
switch(color) {
case Color.Red:
…
break;
case Color.Blue:
…
break;
case Color.Green:
…
break;
default:
break;
}
}
}
メソッドFillの呼出し情報は,指定された色の一つで形を塗りつぶせることを示している。
enumを使用することで,コードの読みやすさ及び自己記述性が向上するため,enumのない言語で通常
使用される整数定数よりも,enumの方が優れている。コードが自己記述的性質を備えているので,開発
ツールも,コードの記述及びその他の設計者の作業を支援できるようになる。例えば,仮引数の型として
intではなくColorを使うことによって,賢いコード編集者にColor値を示唆することができる。
8.12 名前空間及びアセンブリ
これまで提示したプログラムは,System.Consoleのような幾つかのシステムが提供するクラスへの
依存を除いては,それ自体で完結していた。ただし,実世界アプリケーションでは,それぞれ別個にコン
パイルされた,幾つかの異なる部分から構成されることが普通である。例えば,企業のアプリケーション
は,社内で開発したもの,外部のソフトウェア販売元から購入したものなど,様々な部品に依存する場合
がある。
名前空間及びアセンブリは,このような部品基盤システム (component-based system) を可能にする。名
前空間は,論理的に組織化されたシステムを提供する。名前空間は,プログラムの“内向けの”編成シス
テムとしても,他のプログラムに開示するプログラム要素を表す手段としての“外向けの”編成システム
としても,使用される。
アセンブリは,物理的なパッケージ化及び配備のために使用される。アセンブリは,型,型を実装する
ために使われる実行可能コード及び他のアセンブリに対する参照を保持することができる。
名前空間及びアセンブリの使用法を示すために,次に,8.1に示した“hello, world”プログラムをもう一
度使う。メッセージを渡すライブラリ,及びメッセージを表示する端末アプリケーションという二つの部
分に,プログラムを分割する。
48
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
クラスライブラリは,HelloMessageという名前の単一クラスを含める。次に,CSharp.Introduction
という名前の名前空間におけるクラスHelloMessageの例を示す。
// HelloLibrary.cs
namespace CSharp.Introduction
{
public class HelloMessage
{
public string Message {
get {
return "hello, world";
}
}
}
}
クラスHelloMessageは,Messageという名前の読込み専用特性を提供する。名前空間は,入れ子に
でき,次のように宣言できる。
namespace CSharp.Introduction
{…}
これは,次の2重入れ子の名前空間の省略形である。
namespace CSharp
{
namespace Introduction
{…}
}
“hello, world”の部品化の次の段階は,クラスHelloMessageを用いる端末アプリケーションを書く
ことである。このクラスのための完全限定名(10.8.2参照)CSharp.Introduction.HelloMessageを
使うこともできるが,これは長くて不格好である。using名前空間指令を使用すると,簡単にクラス名を
記述できる。名前空間に含まれるすべての型を限定せずに使用できる。次に,名前空間
CSharp.Introductionを参照するusing名前空間指令の例を示す。
// HelloApp.cs
using CSharp.Introduction;
class HelloApp
{
static void Main() {
HelloMessage m = new HelloMessage();
System.Console.WriteLine(m.Message);
}
}
ここに出現するHelloMessage は,CSharp.Introduction.HelloMessageの省略形となる。
C#では,別名を定義して使用することもできる。using別名指令を使用すると,型に対する別名を定義
49
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
できる。別名は,複数のクラスライブラリの間で名前の衝突が発生する状況で,又は大きい名前空間に含
まれる少数の型を使用する場合に,役に立つ。次に,クラスHelloMessageの別名としてMessageSource
を定義するusing別名指令の例を示す。
using MessageSource = CSharp.Introduction.HelloMessage;
ここに書いたコードは,クラスHelloMessageを含むクラスライブラリ及びクラスHelloAppを含む
アプリケーションにコンパイルできる。コンパイル手順の詳細は,使用するコンパイラ又はツールによっ
て異なる場合がある。コマンド行コンパイラは,このライブラリを用いるクラスライブラリ及びアプリケ
ーションを次のようにコンパイルできる。
csc /target:library HelloLibrary.cs
csc /reference:HelloLibrary.dll HelloApp.cs
これは,HelloLibrary.dllという名前のクラスライブラリ及びHelloApp.exeという名前のアプ
リケーションを生成する。
8.13 版管理
版管理とは,互換性を保ちながら部品を発展させていく処理である。ある部品の旧版に依存して動作し
ていたコードが,再コンパイルによってその部品の新版とも動作する場合は,部品の新旧の版にはソース
互換性がある。一方,部品の旧版に依存して動作していたアプリケーションが,再コンパイルせずにその
部品の新版とも動作する場合は,部品の新旧の版にはバイナリ互換性がある。
大部分の言語では,バイナリ互換性を一切もたない。多くの言語では,ソース互換性を容易にする機能
もほとんどない。一部の言語では,クラスを改良するために少なくとも一部のクライアントコードを変更
しなければならないという欠陥がある。
例として,Baseという名前の基底クラスを出荷する状況を考える。第1版では,Baseは,メソッドF
を含まない。Baseから派生するDerivedという名前の部品がFを導入する。このクラスDerivedは,
それが依存するクラスBaseとともに顧客に提供され,顧客は,それを多くのクライアント及びサーバに
配備する。
// Author A
namespace A
{
public class Base
// version 1
{
}
}
// Author B
namespace B
{
class Derived: A.Base
{
public virtual void F() {
System.Console.WriteLine("Derived.F");
}
}
50
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
ここまではよかったが,次に版管理問題が発生する。Baseの作成者が新版を作成し,Baseにそれ自体
のメソッドFを与える。
// Author A
namespace A
{
public class Base
// version 2
{
public virtual void F() {
// added in version 2
System.Console.WriteLine("Base.F");
}
}
}
Baseのこの新版は,初期の版とのソース互換性及びバイナリ互換性をもつ必要がある。メソッドを簡
単に追加できないのでは,基底クラスは発展できない。残念ながら,Baseの新しいFは,DerivedのF
を不明確にする。Derivedは,BaseのFを上書きしようとしていただろうか。Derivedがコンパイル
されたときには,BaseにはFなどなかったので,それはあり得ない。たとえ,DerivedのFがBaseの
Fを上書きするとしても,Baseで規定された契約,すなわち,Derivedが書かれたときにはまだ規定さ
れていなかった契約に従わなければならない。場合によっては,これは不可能となる。例えば,BaseのF
では,上書きするのは常にBaseを呼ばなければならないとなっているかもしれない。DerivedのFは,
このような契約に従うことができない。
C#では,開発者の意図を明確に示すように求めることで,このような版管理に関する問題に対処する。
このコードの元の例では,BaseはFをもたなかったので,コードは明確であった。明らかに,Derived
のFは,Fという名前の基底メソッドなど存在しないので,基底メソッドを上書きするのではなく,新し
いメソッドであることを意図している。
BaseがFを追加して新版を出荷しても,Derivedのバイナリ版の意図は,いまだに明確である。
DerivedのFは,意味的にBaseのFとは無関係であり,上書きとして扱われるべきではない。
ただし,Derivedが再コンパイルされると,意味は不明確となる。Derivedの作成者は,FでBase
のFを上書きするつもりかもしれず,又は,隠ぺいするつもりかもしれない。意図が不明確なので,コン
パイラは警告を出し,省略時の処理としてDerivedのFがBaseのFを隠ぺいするようにする。この処
理は,Derivedが再コンパイルされなかった場合の意味に合致する。生成された警告は,Derivedの作
成者に対してBaseにメソッドFが存在することを注意する。
DerivedのFがBaseのFに意味的に関係ないならば,Derivedの作成者は,この意図をFの宣言中
にキーワードnewを用いて表現し,警告が発生しないようにできる。
// Author A
namespace A
{
public class Base
// version 2
{
public virtual void F() { // added in version 2
51
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
System.Console.WriteLine("Base.F");
}
}
}
// Author B
namespace B
{
class Derived: A.Base
// version 2a: new
{
new public virtual void F() {
System.Console.WriteLine("Derived.F");
}
}
}
反対に,Derivedの作成者が更に検討してDerivedのFがBaseのFを上書きすべきだと決定するか
もしれない。この意図は,次のように,キーワードoverrideを用いて指定できる。
// Author A
namespace A
{
public class Base
// version 2
{
public virtual void F() { // added in version 2
System.Console.WriteLine("Base.F");
}
}
}
// Author B
namespace B
{
class Derived: A.Base
// version 2b: override
{
public override void F() {
base.F();
System.Console.WriteLine("Derived.F");
}
}
}
Derivedの作成者は,もう一つ別の選択肢をもつ。それは,Fの名前を変更することであり,これによ
って名前の衝突を完全に避けることができる。この変更によってDerivedに対するソース互換性及びバ
イナリ互換性は失われるが,互換性の重要性は状況によって異なる。Derivedが他のプログラムによって
52
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
使われていないならば,Fの名前の変更は,プログラムの読みやすさを改善するので望ましい考えである。
Fの意味についての混乱はもはやないからである。
8.14 外部別名
特に明記しない限り,参照アセンブリ及び現在のプログラムに含まれる型は,一つの名前空間階層に置
かれる。単一名前空間階層では,異なるアセンブリから同じ完全限定名で型を参照することはできない。
この状況は,型が独立に同じ名前で与えられた場合,又はプログラムが同じアセンブリの複数の版を参照
する必要がある場合に生じる。外部別名は,そのような状況で,別々の名前空間階層を生成し参照するこ
とを可能にする。
次の二つのアセンブリを考える。
// Assembly a1.dll:
namespace N
{
public class A {}
public class B {}
}
// Assembly a2.dll:
namespace N
{
public class B {}
public class C {}
}
さらに,次のプログラムを考える。
class Test
{
N.A a;
// Ok
N.B b;
// Error
N.C c;
// Ok
}
次のようなコマンド行で,このプログラムのコンパイルが可能になる。
csc /r:a1.dll /r:a2.dll test.cs
ここで,a1.dll 及びa2.dllに含まれる型は,すべて大域名前空間階層に置かれ,型N.Bが,両方の
アセンブリに存在するために,エラーが起こる。外部別名を使えば, a1.dll 及び a2.dllに含まれる
型を別々の名前空間階層に置くことが可能となる。
次のプログラムは,二つの外部別名X及びYを宣言して使っている。X 及びYは,それぞれ,一つ以上
のアセンブリに含まれる型から作られる異なる名前空間階層の根を表現する。
extern alias X;
extern alias Y;
class Test
53
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
X::N.A a;
X::N.B b1;
Y::N.B b2;
Y::N.C c;
}
このプログラムは,外部別名X及びYの存在を宣言するが,別名の実際の定義は,このプログラムの外
にある。例えば,コマンド行コンパイラを用いて,a1.dllの中の型で構成される名前空間の根をXとし
て,a2.dllの中の型で構成される名前空間の根をYとする定義が可能となる。コンパイラは,次のよう
なコマンド行で上の例を可能にする。
csc /r:X=a1.dll /r:Y=a2.dll test.cs
同一名N.Bをもつクラスが,名前空間別名限定子X::N.B及びY::N.Bを用いて,X.N.B及びY.N.B
として参照可能となる。外部別名を宣言するプログラムに対して,外部定義が与えられない場合には,エ
ラーとなる。
一つの外部別名が複数のアセンブリを含むことができ,一つのアセンブリが複数外部別名を含むことも
できる。
// Assembly a3.dll:
namespace N
{
public class D {}
public class E {}
}
csc /r:X=a1.dll /r:X=a3.dll /r:Y=a2.dll /r:Y=a3.dll test.cs
上のアセンブリ及びコマンド行で,a1.dll 及び a3.dllの中の型で構成される名前空間階層の根がX
と定義され,a2.dll及び a3.dllの中の型で構成される名前空間階層の根がYと定義される。このよう
な定義によって,a3.dllのクラスN.DをX::N.D 及び Y::N.Dの両方で参照できる。
アセンブリは,たとえ一つ以上の外部別名に含まれていたとしても,大域的名前空間階層に置くことが
できる。
csc /r:a1.dll /r:X=a1.dll /r:Y=a2.dll test.cs
例えば,上のコマンド行は,アセンブリを,大域的名前階層,及び外部別名Xによって根を参照する名
前階層に置く。その結果,クラスN.Aは,N.A 又は X::N.Aで参照できる。
識別子globalを用いた,global::System.IO.Streamのような名前空間別名限定子によって,探
索が常に大域的名前空間階層の根から始まるように保証することもできる。
using指令を用いると,同じ直上の取り囲む名前空間宣言又はコンパイル単位において定義された外部
別名を参照できる。次に例を示す。
extern alias X;
using X::N;
class Test
54
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
A a;
// X::N.A
B b;
// X::N.B
}
8.15 属性
C#は,命令型言語だが,他のあらゆる命令型言語と同じように,宣言的要素も含まれる。例えば,クラ
スにおけるメソッドのアクセス可能性は,それをpublic,protected,internal,protected
internal又はprivateと宣言することによって指定される。C#では,この機能が一般化されているた
め,プログラマは新種の宣言情報を考え,様々なプログラム実体にその宣言情報を関連付け,実行時に取
り出すことができる。プログラムは,属性(箇条24参照)を定義し使うことによってこの追加宣言情報を
指定できる。
例えば,あるフレームワークでHelpAttributeという,クラス及びメソッドといったプログラム要素
に配置できる属性を定義し,開発者にプログラム要素から関連する文書への対応付けを提供できるように
することができる。次に,一つの順序指定仮引数 (string url) 及び一つの名前付き仮引数 (string
Topic) をもつHelpAttributeという名前で,略称がHelpの属性クラスの例を示す。
using System;
[AttributeUsage(AttributeTargets.All)]
public class HelpAttribute: Attribute
{
public HelpAttribute(string url) {
this.url = url;
}
public string Topic = null;
private string url;
public string Url {
get { return url; }
}
}
順序指定仮引数は,属性クラスの公開インスタンス構築子に対する仮引数によって定義される。名前付
き仮引数は,属性クラスの公開の非静的読み書き可能フィールド及び特性によって定義される。
次に,属性Helpの幾つかの使用例を示す。
[Help("http://www.mycompany.com/…/Class1.htm")]
public class Class1
{
[Help("http://www.mycompany.com/…/Class1.htm", Topic = "F")]
public void F() {}
}
特定のプログラム要素に対する属性情報は,自己反映機能を使って実行時に取り出せる。次に,Class1
が属性Helpをもつかどうか調べて,属性があるならば関連するUrl及びTopicの値を書き出す例を示
す。
55
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class Test
{
static void Main() {
Type type = typeof(Class1);
object[] arr = type.GetCustomAttributes(typeof(HelpAttribute),
true);
if (arr.Length == 0)
Console.WriteLine("Class1 has no Help attribute.");
else {
HelpAttribute ha = (HelpAttribute) arr[0];
Console.WriteLine("Url = {0}, Topic = {1}", ha.Url, ha.Topic);
}
}
}
8.16 総称
C#では,クラス,構造体,インタフェース及びメソッドを,格納し操作するデータの型によって仮引数
化(つまり,仮引数をもたせること)ができる。この機能は,まとめて総称として知られる機能集合とな
る。C#の総称は,Eiffel若しくはAdaの総称の利用者,又はC++のテンプレートの利用者には,馴染み深
い。
多くの一般的なクラス及び構造体は,格納し操作するデータの型によって仮引数化できる。これらを,
総称クラス宣言及び総称構造体宣言と呼ぶ。同様に,多くのインタフェースは,それが扱うデータの型に
よって仮引数化可能な契約を定義できる。これを総称インタフェース宣言と呼ぶ。“総称アルゴリズム”を
実装するために,メソッドも型によって仮引数化できる。そのようなメソッドを,総称メソッドと呼ぶ。
8.16.1 なぜ総称か
総称がない場合,プログラマは,基底型objectの変数を使って任意の型のデータを格納できる。説明
のために,二つの動作“Push”及び“Pop”をもつ単純な型Stackを作る。クラスStackは,そのデー
タをobjectの配列に格納し,メソッドPush 及びメソッド Popは,型objectを用いてデータを受理
及び返却する。
public class Stack
{
private object[] items = new object[100];
public void Push(object data) {…}
public object Pop() {…}
}
そうすると,スタック上に,例えば,型Customerのような任意の型の値を格納できる。しかし,その
値を取り出そうとすると,メソッドの結果に対して型objectを型Customerへと明示的にキャストする
ことが必要となる。これは,書くのが面倒なだけでなく,実行時の型検査という性能上の不利益まで生じ
る。
56
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Stack s = new Stack();
s.Push(new Customer());
Customer c = (Customer)s.Pop();
メソッドPushにintのような値型を渡そうとすれば,自動的にボックス化される。同様に,スタック
からintを取り出そうとすれば,メソッドPopから得た型objectに対して明示的にボックス化解除を
する必要がある。
Stack s = new Stack();
s.Push(3);
int i = (int)s.Pop();
このようなボックス化及びボックス化解除操作も性能に影響する可能性がある。
さらに,この実装では,スタックに置くデータの種類を限定することができない。実際,スタックを作
って,型Customerを入れることはできるが,その後で,同じスタックからデータを取り出して,両立し
ない型にキャストしようとすることを止められない。
Stack s = new Stack();
s.Push(new Customer());
Employee e = (Employee)s.Pop();
// runtime error
このコードは,クラスStackの本来の実装意図からは不適切な使用であり,コンパイル時エラーとなる
のが望ましいのに,実際には正当なコードとなる。実行時には,しかし,不当なキャスト演算を行おうと
したので,失敗する。
8.16.2 総称の生成及び消費
総称は,コンパイラ及び/又は実行エンジンによって,使用する型に特化した高性能データ構造を生成
する機能を提供する。これらのいわゆる総称型宣言は,内部アルゴリズムは同じでありながら,その外部
インタフェース及び内部データが,利用者の選択に応じて変化するように作られる。
開発者の学習曲線を最小化することを目指して,総称は,C++のテンプレートと同じように使われる。
プログラマは,クラス及び構造体を通常と同じように作り,山括弧表記(< 及び >)を用いて型仮引数を
指定できる。総称クラス宣言が用いられるときは,型仮引数は,クラスの利用者が与える型実引数によっ
て置き換えられなければならない。
次の例では,総称クラス宣言Stackにおいて,ItemTypeと呼ばれる型仮引数をクラス宣言の後の山
括弧で宣言する。総称クラスStackのインスタンスは,objectからの型変換を強制するのではなくて,
実際に作られたときの型を受け入れて,変換せずにその型のデータを格納する。型仮引数ItemTypeは,
実行時のデータが指定されるまでは場所取りとして振る舞う。ItemTypeが,内部要素配列のための要素
型,メソッドPushの仮引数のための型,及びメソッドPopの返却型として使われることに注意する。
public class Stack<ItemType>
{
private ItemType[] items = new ItemType[100];
public void Push(ItemType data) {…}
public ItemType Pop() {…}
}
次の短い例におけるように,総称クラス宣言Stackを使えば,総称クラスで使われる実行時の型を指定
できる。この場合,名前の後に山括弧を用いて型実引数として実行時の型を指定することによって,型int
57
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
をStackに用いるよう命令する。
Stack<int> s = new Stack<int>();
s.Push(3);
int x = s.Pop();
このようにして,新しく構築型Stack<int>を作った。Stackの宣言内部におけるすべてのItemType
は,与えられた型実引数intによって置き換えられる。実際に,Stack<int>の新しいインスタンスを作
成すると,その要素配列は,object[]ではなくint[]になり,記憶域効率をかなり向上させる。さらに,
スタック上にintを入れる際のボックス化の無駄も省くことができた。その上,スタックから要素を取り
出すときにも,クラスStackがそのデータ構造としてintを格納しているので,明示的なキャストが不
必要となる。
Stackにint以外の要素を格納したいときには,新しい型実引数を指定して,Stackから異なる構築
型を作成する必要がある。単純な型Customerがあって,それを格納するのにStackを使いたいと仮定
すると,そのために,型実引数にクラスCustomerを用いることによって,既に作ったプログラムを再利
用できる。
Stack<Customer> s = new Stack<Customer>();
s.Push(new Customer());
Customer c = s.Pop();
もちろん,型実引数として型CustomerをもつStackを作ったら,オブジェクトCustomer(又は,
Customerから派生したクラスのオブジェクト)しか格納することができない。総称は,強い型付けを提
供するので,次の例に示すように,そのようなスタックに不当に整数を格納するようなことはできない。
Stack<Customer> s = new Stack<Customer>();
s.Push(new Customer());
s.Push(3);
// compile-time error
Customer c = s.Pop();
// no cast required
8.16.3 複数の型仮引数
総称型宣言は,任意個の型仮引数をもつことができる。Stackの例では,一つの型仮引数しか用いなか
った。値とキーとを格納する総称クラス宣言Dictionaryを作ったと仮定しよう。総称版のDictionary
を,次のように,宣言の山括弧内でコンマで区切られた二つの型仮引数宣言をもつように定義できる。
public class Dictionary<KeyType, ElementType>
{
public void Add(KeyType key, ElementType val) {…}
public ElementType this[KeyType key] {…}
}
このDictionaryを使うときには,山括弧に二つの型実引数を与える必要がある。関数Add又は添字
子を使うときには,コンパイラが正当な型を与えているかを検査する。
Dictionary<string, Customer> dict = new Dictionary<string, Customer>();
dict.Add("Peter", new Customer());
Customer c = dict["Peter"];
8.16.4 制約
多くの場合に,与えられた型仮引数に基づいてデータを格納するだけでは済まない。型仮引数のメンバ
58
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
を使って総称型宣言の内部にある文を実行しようとすることも多い。
例えば,上のDictionaryのメソッドAddにおいては,メソッドCompareToで,与えられたキーを
使って要素を次のように比較しようとする。
public class Dictionary<KeyType, ElementType>
{
public void Add(KeyType key, ElementType val)
{
…
if (key.CompareTo(x) < 0) {…}
// compile-time error
…
}
}
残念なことに,コンパイル時には,型仮引数KeyTypeは,予想されるとおり総称である。コンパイラ
は,書かれているとおりに,ToStringのようなobjectに使用可能な演算だけが型KeyTypeの変数key
に使用可能と仮定する。結果として,コンパイラは,この変数にメソッドCompareToが見つからないの
で,エラーを表示する。しかし,プログラムをコンパイル可能とするために,key変数を,IComparable
インタフェースのようなCompareToメソッドを含む型にキャストできる。
public class Dictionary<KeyType, ElementType>
{
public void Add(KeyType key, ElementType val)
{
…
if (((IComparable)key).CompareTo(x) < 0) {…}
…
}
}
ところが,Dictionaryの構築型をIComparableを実装しない型実引数を与えて作ると,
InvalidCastExceptionのような実行時エラーを生じる。総称の目標が強い型付けを提供してキャスト
の必要性を減らすことだったので,より優れた解法が必要となる。
型仮引数に対して,省略可能な制約の並びを与えることができる。制約は,型実引数として受理される
のに型が満たさなければならない要件を示す。(例えば,与えられたインタフェースを実装しなければなら
ないとか,与えられた基底クラスから派生されなければならないなど。)制約は,語whereの後ろに,型
仮引数,コロン (:),その後に,コンマで区切られた制約の並びを続けて宣言される。制約には,クラス
型,インタフェース型,他の型仮引数,参照型制約“class”,値型制約“struct”,及び構築子制約“new()”
を含むことができる。
Dictionaryの内部でメソッドCompareToを使いたいという要求を満たすには,KeyTypeに対して,
Dictionaryの第1引数として渡される型は,IComparableを実装しなければならないという制約を次
のように課すことができる。
public class Dictionary<KeyType, ElementType> where KeyType:
IComparable
59
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public void Add(KeyType key, ElementType val)
{
…
if (key.CompareTo(x) < 0) {…}
…
}
}
コンパイルされると,このコードは,型Dictionaryが構築されるごとに,最初の型実引数として
IComparableを実装するものが渡されることを保証するかどうか検査する。さらに,メソッド
CompareToを呼び出す前に,変数keyに対して明示的にIComparableへのキャストを行う必要がなく
なる。
制約は,フレームワーク,すなわち,関連クラスの集合を定義する文脈で最も有用となる。それは,共
通の呼出し情報及び/又は基底型に基づいた多数の型を保証することができるからである。制約は,異な
る型によって提供される機能性をまとめあげた“総称アルゴリズム”を定義するのに役に立つ。このこと
自体は,下位クラス構造化及び実行時多相性によっても実現できるが,多くの場合に,静的な制約による
多相性の方が総称アルゴリズムのより効率的なコード及びより柔軟な仕様を与え,実行時ではなくコンパ
イル時によって多くのエラーを捕まえることができる。しかし,制約の使用には注意が必要である。制約
を実装しない型を総称コードと一緒に用いることは容易ではない。
型仮引数に対して,インタフェース及び型仮引数を幾つでも制約として指定できるが,クラスについて
は一つしか指定できない。制約付き型仮引数は,別々のwhere節をもつ。次の例において,型仮引数
KeyTypeは,二つのインタフェース制約をもつが,型仮引数ElementTypeは,一つのクラス型制約を
もつ。
public class Dictionary<KeyType, ElementType >
where KeyType: IComparable, IEnumerable
where ElementType: Customer
{
public void Add(KeyType key, ElementType val)
{
…
if (key.CompareTo(x) < 0) {…}
…
}
}
8.16.5 総称メソッド
場合によっては,型仮引数がクラス全体に対して必要とはならずに,特定のメソッドを呼び出すときに
だけ必要となることがある。これは,総称型を仮引数とするメソッドを作る場合にしばしば起こる。例え
ば,8.16.2で示したStackを用いるときに,スタック上に複数の値を立て続けに入れたいことがあり,一
つの呼出しでそれをするメソッドを書こうと思うことがある。例えばStack<int>のような,単一の種類
のStackを使うだけならば,そのようなメソッドを書くことは,次のように易しい。
60
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
static void PushMultiple(Stack<int> s, params int[] values) {
foreach (int v in values) {
s.Push(v);
}
}
このメソッドを用いて,Stack<int>に複数の値を入れることができる。
Stack<int> s = new Stack<int>();
PushMultiple(s, 1, 2, 3, 4);
しかし,このメソッドは,特定の構築型Stack<int>でしか働かない。他の同様なStackからの構築
型についても同様のコードを書くことは容易だが,型実引数がどのようなものであっても,任意のStack
で働くような単一のメソッドを書きたい。
これを,総称メソッドを書くことによって行う。総称クラス宣言と同様に,総称メソッドは,山括弧で
囲まれた型仮引数を使って書く。総称メソッドでは,型仮引数は,メソッド名の直後に書かれ,仮引数並
び,返却型及びメソッド本体で用いることができる。総称メソッドPushMultipleは,次のようになる。
static void PushMultiple<ItemType>(Stack<ItemType> s,
params ItemType[] values)
{
foreach (ItemType v in values) {
s.Push(v);
}
}
この総称メソッドを用いると,任意の種類のStackに複数の要素を入れることができる。さらに,コン
パイラの型検査が,入れられた要素が使われているStackの種類に応じた正当なものであることを保証す
る。総称メソッドの呼出しには,山括弧の中に型実引数を入れてメソッドに与える。総称メソッド
PushMultipleは,次のように呼び出される。
Stack<int> s = new Stack<int>();
PushMultiple<int>(s, 1, 2, 3, 4);
この総称メソッドPushMultipleは,任意の種類のStackで働くので,前の版のよりも優れている。
しかし,メソッドの型実引数として,望ましいItemTypeを与えなければならないので,若干わずらわし
く感じる。しかし,多くの場合に,型推論と呼ばれる処理を用いることによって,メソッドに渡される他
の実引数から,正しい型実引数をコンパイラが推論することができる。この例では,最初の実引数が,型
Stack<int>であり,引き続く実引数が型intなので,コンパイラは,型仮引数もintでなければなら
ないと推論することができる。したがって,総称メソッドPushMultipleは,型仮引数を指定せずに次
のように呼び出すことができる。
Stack<int> s = new Stack<int>();
PushMultiple(s, 1, 2, 3, 4);
8.17 無名メソッド
C#のコードでは,コールバックメソッドは,委譲によって呼び出されることが多く,直接呼び出される
ことはまずない。このような場合,メソッド宣言を委譲のインスタンス化と切り離さなければならないの
で,メソッドの目的が分かりにくくなる。対照的に,無名メソッドの本体は,委譲が使われる場所にコー
61
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ドを展開して書かれることになり,メソッドのソースコードと委譲インスタンスとが便利に結び付けられ
る。この利便性に加えて,無名メソッドは,それを含む関数メンバの局所状態へのアクセスを共有できる。
名前付きメソッドを用いて,この状態共有を実現すると,局所変数をオブジェクトのフィールドに“もち
上げる”必要があるので,ソースコードが更に分かりにくくなる。
無名メソッドは,次のように,≪無名メソッド式≫を用いて定義される。
≪無名メソッド式≫:
delegate ≪無名メソッド呼出し情報≫opt ≪ブロック≫
省略可能な≪無名メソッド呼出し情報≫は,無名メソッドの仮引数の名前及び型を定義する。≪無名メ
ソッド呼出し情報≫が省略されると,≪ブロック≫では,一切の仮引数を用いない。≪ブロック≫は,無
名メソッドの本体を定義する。
≪無名メソッド式≫は,無名メソッドを参照する特別な種類の値として分類される。この値には,本質
的な意味での型はないが,無名メソッドに適合する仮引数の型及び返却型をもつ委譲型に暗黙に変換可能
とする。呼出し情報なしに無名メソッドが宣言されると,その無名メソッドには,out仮引数を含まない
任意の委譲仮引数型が適合する。呼出し情報付きで無名メソッドが宣言されると,その型及び(仮引数の)
順番に正確に合致する委譲仮引数型だけが適合する。委譲の返却型が無名メソッドに適合するのは,無名
メソッドのすべてのreturn文に伴う式が暗黙に委譲の返却型に変換される場合とする。委譲返却型void
は,return文をもたないか,式を伴わないreturn文だけをもつ無名メソッドと適合する。
有効範囲が無名メソッド宣言を含む(thisを含めた)局所変数及び値仮引数は,無名メソッドの外変
数と呼ばれる。
無名メソッドがないとき,局所変数又は値仮引数の生存期間は,12.1.7に示されているように,その有
効範囲が終わるときに終わる。しかし,実行が外変数の有効範囲を去った後も,無名メソッドは,その外
変数インスタンスにアクセスできる。この場合,外変数の生存期間は,すべての参照している無名メソッ
ドの委譲が,ゴミ集め可能となるまで,延長される(10.9参照)。
無名メソッドでは,有効範囲の外にあるref仮引数又はout仮引数をアクセスできない。この理由は,
関数メンバの呼出し側が,そのような仮引数の記憶領域を割り当てるので,呼び出された関数メンバでは,
その生存期間を任意に延長することができない。structのインスタンスメソッドのthis値は,ref仮
引数と等価なので,必然的にstructの無名メソッドは,thisにアクセスすることを許されない。
意味的には,クラス又は構造体Tに含まれる無名メソッドは,Tのメソッドとみなされる。Tがクラス
型ならば,無名メソッドは,包含する側の関数メンバが,インスタンスメンバ又は静的メンバのいずれか
に応じて,インスタンスメソッド又は静的メソッドとみなされる。対照的に,Tが構造体型ならば,無名
メソッドは,常に静的と考えられ,上に示されたように,thisにアクセスできない。
次のコードは,委譲型Action及びメソッドWalkを定義する。メソッドWalkは,リンクされた並び
の中の節点上で,動作がfalseを返すか,並びの終点に到達するまで,逐次的に動作を呼び出す。
delegate bool Action(Node n);
static void Walk(Node n, Action a) {
while (n != null && a(n)) n = n.Next;
}
次のWalkの呼出しは,並びの中の節点の名前を表示する無名メソッドを表示するのに使われる。
Walk(list,
delegate(Node n) {
62
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine(n.Name);
return true;
}
);
このコードは,名前付きメソッドを使って容易に実装できるが,そうすると,メソッド宣言を呼出しか
ら分ける必要があり,メソッドの目的及び呼出しの結果を不明りょうにする。
Walk(list, new Action(DisplayNodeName));
…
bool DisplayNodeName(Node n) {
Console.WriteLine(n.Name);
return true;
}
次のコードは,各節点の順番を表示するために外変数の局所変数(以後,外局所変数という。)cを用い
る。
int c = 0;
Walk(list,
delegate(Node n) {
Console.WriteLine("{0}: {1}", ++c, n.Name);
return true;
}
);
Console.WriteLine("Processed {0} nodes", c);
この例を名前付きメソッドを用いて実装すると,外局所変数cをフィールドに“もち上げる”必要があ
り,それは,コードを分かりにくくし,型に対する追加的な手間が発生して,並行処理の問題を生じる。
次のコードは,外局所変数c及びインスタンスフィールドmaxを用いて,表示される項目の個数を制限
する。
class A
{
int max;
…
void F(Node list) {
int c = 0;
Walk(list,
delegate(Node n) {
if (c >= max) {
Console.WriteLine("... display truncated");
return false;
}
Console.WriteLine("{0}: {1}", ++c, n.Name);
return true;
63
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
);
Console.WriteLine("Processed {0} nodes", c);
}
}
Fは,クラスAのインスタンスメソッドなので,無名メソッドは,Aのインスタンスメソッドと考えら
れ,フィールドmaxは,無名メソッドのthis変数(これは,Fのthis変数と同じである。)でアクセス
される。すなわち,maxはフィールドであって,外変数ではなく,無名メソッドは,maxを他のインスタ
ンスメソッドと同じようにアクセスする。
この例を,無名メソッドなしに(並行性の可能性を考慮して)安全に実装するには,大量の作業が必要
であり,明せき(晰)性を損なうだろう。
class A
{
int max;
…
void F(Node list) {
NodeNameDisplayer nnd = new NodeNameDisplayer(this);
nnd.c = 0;
Walk(list, new Action(nnd.DisplayNodeName));
Console.WriteLine("Processed {0} nodes", nnd.c);
}
internal class NodeNameDisplayer
{
A outer;
internal int c;
public NodeNameDisplayer(A outer) {
this.outer = outer;
}
bool DisplayNodeName(Node n) {
if (c >= outer.max) {
Console.WriteLine("... display truncated");
return false;
}
Console.WriteLine("{0}: {1}", ++c, n.Name);
return true;
}
}
}
8.18 反復子
foreach文は,列挙可能集団の要素上で反復するために使われる。集団が,列挙可能であるためには,
64
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
列挙子を返す仮引数のないメソッドGetEnumeratorをもたなければならない。一般に,プログラマによ
る列挙子の実装は困難だが,反復子によって大幅に単純化される。
反復子は,値の順序付き列を産出する文のブロックとなる。反復子は,一つ以上のyield文の存在によ
って,通常の文から区別される。
− yield return文は,反復の次の値を産出する。
− yield break文は,反復が完了したことを示す。
反復子は,関数メンバの返却型が,列挙子インタフェースの一つ又は列挙可能インタフェースの一つで
ある限り,関数メンバの本体として用いることができる。
− 列挙子インタフェースは,System.Collections.IEnumeratorであって,
System.Collections.Generic.IEnumerator<T>から構築される型をもつ。
− 列挙可能インタフェースは,System.Collections.IEnumerableであって,
System.Collections.Generic.IEnumerable<T>から構築される型をもつ。
反復子は,メンバの一種でないが,関数メンバを実装する手段であることを,理解するのが重要である。
反復子によって実装されるメンバは,他のメンバによって上書き又は多重定義できる。“他のメンバ”は,
反復子で実装されたものでもよいし,そうでないものでもよい。
次のクラスStack<T>は,反復子を用いてメソッドGetEnumeratorを実装する。これは,スタックの
要素を上から下への順序で列挙する。
using System.Collections.Generic;
public class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
public void Push(T data) {…}
public T Pop() {…}
public IEnumerator<T> GetEnumerator() {
for (int i = count ‒ 1; i >= 0; --i) {
yield return items[i];
}
}
}
メソッドGetEnumeratorの存在は,Stack<T>を列挙可能型にして,Stack<T>のインスタンスを
foreach文で用いられるようにできる。次の例は,整数スタック上に値0から9を入れて,foreachル
ープを使って,上から下への順序で値を表示する。
using System;
class Test
{
static void Main() {
Stack<int> s = new Stack<int>();
for (int i = 0; i < 10; i++) s.Push(i);
foreach (int i in s) Console.Write("{0} ", i);
65
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine();
}
}
この出力は次となる。
9 8 7 6 5 4 3 2 1 0
foreach文は,暗黙に,集団の仮引数なしメソッドGetEnumeratorを呼び出して,列挙子を得る。
一つの集団には,一つの仮引数なしメソッドGetEnumeratorしか定義できないが,複数の列挙方法があ
って,仮引数によって列挙方式を制御するのが適切なことも多い。そのような場合に,集団は,反復子を
用いて,列挙可能インタフェースの一つを返す特性又はメソッドを実装することができる。例えば,
Stack<T>は,型IEnumerable<T>の二つの新しい特性TopToBottom 及び BottomToTopを導入でき
る。
using System.Collections.Generic;
public class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
public void Push(T data) {…}
public T Pop() {…}
public IEnumerator<T> GetEnumerator() {
for (int i = count ‒ 1; i >= 0; --i) {
yield return items[i];
}
}
public IEnumerable<T> TopToBottom {
get {
return this;
}
}
public IEnumerable<T> BottomToTop {
get {
for (int i = 0; i < count; i++) {
yield return items[i];
}
}
}
}
特性TopToBottomのgetアクセス子は,スタックそのものが列挙可能オブジェクトなので,ただthis
を返す。特性BottomToTopは,反復子を用いて実装した列挙可能オブジェクトを返す。次の例は,スタ
ック要素をいずれかの順序で列挙するのに,特性をどのように使うことができるかを示す。
using System;
66
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class Test
{
static void Main() {
Stack<int> s = new Stack<int>();
for (int i = 0; i < 10; i++) s.Push(i);
foreach (int i in s.TopToBottom) Console.Write("{0} ", i);
Console.WriteLine();
foreach (int i in s.BottomToTop) Console.Write("{0} ", i);
Console.WriteLine();
}
}
もちろん,これらの特性は,foreach文の外でも使うことができる。次の例では,別のメソッドPrint
に特性を呼び出した結果を渡す。この例では,仮引数を取るメソッドFromToByの本体として反復子が使
われることも示す。
using System;
using System.Collections.Generic;
class Test
{
static void Print(IEnumerable<int> collection) {
foreach (int i in collection) Console.Write("{0} ", i);
Console.WriteLine();
}
static IEnumerable<int> FromToBy(int from, int to, int by) {
for (int i = from; i <= to; i += by) {
yield return i;
}
}
static void Main() {
Stack<int> s = new Stack<int>();
for (int i = 0; i < 10; i++) s.Push(i);
Print(s.TopToBottom);
Print(s.BottomToTop);
Print(FromToBy(10, 20, 2));
}
}
この例の出力は次となる。
9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9
10 12 14 16 18 20
総称列挙可能インタフェース及び総称でない列挙可能インタフェースの両方は,実引数をとらず列挙子
67
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
インタフェースを返すメソッドGetEnumeratorという単一メンバを含む。列挙可能は,列挙子ファクト
リ(FACTORY)として働く。適切に実装された列挙可能は,メソッドGetEnumeratorが呼ばれるたびに独
立な列挙子を生成する。GetEnumeratorへの二つの呼出しの間に列挙可能の内部状態が変化しないと仮
定すると,この二つの列挙子は同じ値の集合を同じ順序で生成するはずである。これは,次の例のように,
列挙子の生存期間が重複している場合でも成り立つのが望ましい。
using System;
using System.Collections.Generic;
class Test
{
static IEnumerable<int> FromTo(int from, int to) {
while (from <= to) yield return from++;
}
static void Main() {
IEnumerable<int> e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}
このコードは,1から10までの整数の単純な掛け算表を印刷する。メソッドFromToが列挙可能eを生
成するためにただ一度だけ呼び出されることに注意する。一方,e.GetEnumerator()は,(foreach文
によって)複数回呼び出されて,複数の等価な列挙子を生成する。これらの列挙子すべては,FromToの
宣言で規定された反復子コードをカプセル化する。反復子のコードはfrom仮引数を変更することに注意
する。それにもかかわらず,それぞれの列挙子には,仮引数from及びtoのそれ自体のコピーが与えら
れるので,列挙子は独立に動作する。列挙子の間での過渡的状態の共有は,列挙可能及び列挙子を実装す
るときに避けた方がよい幾つかの欠陥の一つである。反復子は,このような問題を回避して,頑健な列挙
可能及び列挙子を単純で直感的な方法で実装できるように設計されている。
8.19 null許容型
値型も含めてすべての型でnull許容型を使えるようにすることは,データベースを扱う場合に本質的
に重要である。しかし,歴史的には,はん(汎)用プログラム言語は,この分野で,一切の機能を提供し
ないか,わずかな機能しか提供してこなかった。言語で直接扱えなくても,null及び値型を扱うには,
多くの方法があるのだが,それらにはすべて欠点がある。例えば,ある方法は,“特殊な”値(例えば,整
数での−1)を使ってnullを示すが,これは使われない値が分かっている場合にだけうまく働く。別の方
法では,別のフィールド又は変数にnullかどうかを示す真偽値を保持するが,これでは,仮引数や返却
値の場合をうまく扱えない。第3の方法として,利用者が定義したnull許容型を用いる方法があるが,
これは型の集合が閉じている場合にだけ働く。C#のnull許容型は,この長らく未解決だった問題を,す
べての値型にnull許容形式に対する完全で統合された処理方式を提供することによって解決する。
68
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
null許容型は,型修飾子“?”を用いて構成する。例えば,int? はあらかじめ定義された型intの
null許容形式である。null許容型の基礎となる型は,null許容でない値型でなければならない。
null許容型は,基礎となる型の値にnullを示す真偽値を組み合わせた構造となる。null許容型のイ
ンスタンスは,型boolのHasValue及びnull許容型の基礎となる型のValueという,二つの公開読
取り専用特性をもつ。HasValueは,nullでないインスタンスには真とし,nullインスタンスには偽と
する。HasValueが真のとき,特性Valueは,含まれる値を返す。HasValueが偽のとき,特性Value
へのアクセスは,例外を送出する。
null許容でない任意の値型から,その型のnull許容形式への暗黙の型変換が存在する。さらに,型
null(11.2.7参照)から任意のnull許容型への暗黙の型変換も存在する。次に例を示す。
int? x = 123;
int? y = null;
if (x.HasValue) Console.WriteLine(x.Value);
if (y.HasValue) Console.WriteLine(y.Value);
int値123及びnullリテラルが,暗黙にnull許容型int?に変換される。この例は,xに対して123
を出力するが,その次のConsole.WriteLineは,HasValueが偽なので実行されない。
null許容変換及びもち上げ変換は, null許容でない値型に対するあらかじめ定義された変換又は利
用者定義の変換が,その型のnull許容形式に対しても使用できるようにする。同様に,もち上げ演算子
は, null許容でない型に対するあらかじめ定義された演算子又は利用者定義の演算子を,その型のnull
許容形式に対しても使用できるようにする。
null許容でない値型Sからnull許容でない値型Tへのすべてのあらかじめ定義された変換について,
自動的に,あらかじめ定義されたS? から T?へのnull許容変換が存在する。このnull許容変換は,基
礎となる変換のnull伝ぱ(播)形式という。この変換では,変換元のnull値は,直接,変換先のnull
値になるが,それ以外の値は,基礎となるnull許容でない変換による。さらに,S からT?へ,及びS?
から Tへというnull許容変換が存在する。後者のnull許容変換は,元の値がnullならば,例外を送
出する明示的変換となる。
次に,null許容変換の幾つかの例を示す。
int i = 123;
int? x = i;
// int --> int?
double? y = x;
// int? --> double?
int? z = (int?)y;
// double? --> int?
int j = (int)z;
// int? --> int
利用者定義の変換演算子は,変換の前後の型が共にnull許容でない値型のときに,もち上げ形式をも
つ。もち上げ形式を生成するには,修飾子?を,変換の前後の型に付加する。あらかじめ定義されたnull
許容変換と同様に,もち上げ変換演算子は,nullを伝ぱ(播)する。
比較演算子以外の演算子は,演算対象型及び結果型がすべてnull許容でない値型のときに,もち上げ
形式をとる。比較演算子以外の演算子に対して,すべての演算対象型及び結果型に,修飾子?を付加する
と,もち上げ形式が作られる。例えば,二つのintの演算対象をとって,intを返すあらかじめ定義され
た演算子+のもち上げ形式は,二つのint?の演算対象をとって,int?を返す演算子となる。もち上げ変
換と同様に,比較演算子以外のもち上げられた演算子は,null伝ぱ(播)をする。すなわち,もち上げ
演算子のいずれかの演算対象がnullならば,結果はnullとなる。
69
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
次の例では,二つのint?値を加えるのにもち上げ演算子+を用いる。
int? x = GetNullableInt();
int? y = GetNullableInt();
int? z = x + y;
zへの代入は,次に相当する。
int? z = x.HasValue && y.HasValue ? x.Value + y.Value : (int?)null;
null許容でない値型からそのnull許容形式への暗黙変換が存在するので,もち上げ演算子は,一つ
でも演算対象がnull許容型の場合に適用できる。次の例は,上の例と同じもち上げ演算子+を用いる。
int? x = GetNullableInt();
int? y = x + 1;
xがnullならば,yにはnullが代入される。そうでなければ,yにはxに1を加えた値が代入される。
C#のnull許容変換,もち上げ変換及び比較演算子以外のもち上げられた演算子のnull伝ぱ(播)意
味論は,SQLにおける対応する変換及び演算子によく似ている。しかし,C#のもち上げ比較演算子は,SQL
のような三値論理を導入せず,通常の真偽値の結果を生成する。
比較演算子(==,!=,<,>,<=,>=)は,演算対象型が共にnull許容でない値型であり,結果型がbool
であるときに,もち上げ形式をもつ。比較演算子のもち上げ形式は,(結果型には付加せずに)演算対象型
に修飾子? を付加して作られる。演算子== 及び !=のもち上げ形式は,二つのnull値を等しいと考え,
null値は非null値と等しくないと考える。演算子<,>,<=及び>=のもち上げ形式は,一つ又は両方の
演算対象がnullならば,偽を返す。
演算子== 又は !=のいずれかの演算対象がnull型(11.2.7参照)のとき,もう一つの演算対象は,基
礎となる値型が実際にその演算子を宣言しているかどうかには関係なく,任意のnull許容型であること
が許される。演算子== 又は !=の実装が存在しない場合には,演算対象の特性HasValueを検査するこ
とで代用される。この規則の効果は,次のような文で示される。
if (x == null) Console.WriteLine("x is null");
if (x != null) Console.WriteLine("x is non-null");
これらは,任意のnull許容型又は参照型のxについて許されるので,nullの可能性があるすべての
型のnull検査を行う一般的な方法を与える。
新しくnull判定選択演算子??が提供されている。a ?? bの結果は,aがnullでなければ,aとし,
nullであれば, bとする。直感的には,aがnullのときにbが値を与える。
aがnull許容型であり,bがnull許容でない型のときは,a ?? bは,演算対象型の間に適切な暗黙
変換が存在するならば, null許容でない値を返す。次に例を示す。
int? x = GetNullableInt();
int? y = GetNullableInt();
int? z = x ?? y;
int i = z ?? -1;
x ?? yの型は,int?となるが,z ?? -1の型はintとなる。後者の演算は,型から?を取り除き,
同時に,nullの場合に使われる省略時の値を与えるので,便利である。
null判定選択演算子は,参照型に対しても働く。次に例を示す。
string s = GetStringValue();
Console.WriteLine(s ?? "Unspecified");
70
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
これは,sの値を出力するか,sがnullの場合には,Unspecifiedを出力する。
9
字句構造
9.1
プログラム
C#プログラムは,正式には,コンパイル単位(16.1参照)という,一つ以上のソースファイルからなる。
ソースファイルは,Unicode文字の順序付けられた列とする。ソースファイルは,普通,ファイルシステ
ムのファイルと一対一に対応するが,この対応は必す(須)ではない。
概念的には,プログラムのコンパイルは 次の三つの手順で行われる。
1) 変換 特定の文字集合及び符号化方式を使用したファイルを,Unicode文字を使用したファイルに変
換する。
2) 字句分析 Unicode入力文字ストリームを字句ストリームに変換する。
3) 構文分析 字句ストリームを実行可能なコードに変換する。
適合実装は,(Unicode標準で定義されている)UTF-8符号化形式で符号化されたUnicodeソースファイ
ルを受理して,それをUnicode文字の列に変換しなければならない。実装担当者は,(UTF-16,UTF-32又
はUnicode以外の符号化のような)追加的な符号化方式を受理して変換することができる。
注記 Unicode以外の文字表現を用いたファイルからUnicode文字への変換を定義することは,この規
格の範囲外である。しかし,そのような変換においては,他の文字集合における通常の行分離
文字(又は文字列)がUnicode復帰文字の次にUnicode行送り文字が続く2字の並びに変換さ
れることが望ましい。ほとんどの場合,この変換には目に見えた効果はないが,(複数行にわた
る)逐語的文字列リテラル字句(9.4.4.5参照)に影響する。推奨した方式に従えば,異なった
Unicode以外の文字集合の,特に,行分離の文字列が異なるようなシステムの間でソースファ
イルを移動したときに,逐語的文字列リテラルが同じ文字列を生成できるはずである。
9.2
文法
この規格では,C#プログラム言語の構文を二つの文法を使って表す。字句文法(9.2.1参照)は,行終端
子,空白類,注釈,字句及び前処理指令が,どのようにUnicode文字を組み合わせて定義できるかを示す。
構文文法(9.2.2参照)は,字句文法で得られた字句をどのように組み合わせればC#プログラムとなるか
を規定する。
9.2.1
字句文法
C#の字句文法を9.3,9.4及び9.5で示す。字句文法の終端記号は,Unicode文字集合の文字とし,字句
文法は,どのように文字を組み合わせて字句(9.4参照),空白類(9.3.3参照),注釈(9.3.2参照)及び前
処理指令(9.5参照)を形成するかを規定する。
C#プログラムのソースファイルは,すべて,字句文法の生成規則≪入力≫(9.3参照)に適合しなけれ
ばならない。
9.2.2
構文文法
C#の構文文法については,箇条9〜箇条27及び附属書Aで規定する。構文文法の終端記号は,字句文
法で定義される字句とする。構文文法では,字句を組み合わせてC#プログラムを構成する方法を指定する。
C#プログラムのソースファイルは,すべて,構文文法の生成規則≪コンパイル単位≫(16.1参照)に適
合しなければならない。
9.2.3
文法のあいまい性
≪単純名≫(14.5.2参照)及び≪メンバアクセス≫(14.5.4参照)のための生成規則は,式のための文法
71
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
上のあいまい性を生じさせる可能性がある。
例 F(G<A, B>(7));
この文は,G < A及びB > (7)の二つの実引数をもつFを呼び出すと解釈できる。又は,二つの
型実引数及び一つの実引数をもつメソッドGを呼び出す一つの実引数をもつFを呼ぶとも解釈で
きる。
字句の並びを≪単純名≫(14.5.2参照),≪メンバアクセス≫(14.5.4参照)又は最後に≪型実引数並び
≫(25.5.1参照)をもつ≪ポインタメンバアクセス≫(27.5.2参照)として文脈内において解釈した場合,
閉じている>字句が直後に続く字句が検査される。それが,
( ) ] : ; , . ? == !=
のどれか一つであった場合,≪型実引数並び≫は,≪単純名≫,≪メンバアクセス≫又は≪ポインタメン
バアクセス≫の一部として保持される。そして,他に可能性のある字句の並びの構文解析は,廃棄される。
さもなければ,例え,字句の並びが他に構文解析できないとしても≪型実引数並び≫は,≪単純名≫,≪
メンバアクセス≫又は≪ポインタメンバアクセス≫の一部とみなされない。
注記 これらの規則は,≪名前空間名又は型名≫(10.8参照)における≪型実引数並び≫を構文解析
する場合は,適用されない。
例
F(G<A, B>(7));
この文は,規則に従うと二つの型実引数及び一つの実引数をもつ総称メソッドGを呼ぶ一つの
実引数をもつFを呼ぶと解釈されるだろう。
F(G<A, B>7);
F(G<A, B>>7);
これらの文は,二つの実引数をもつFを呼び出すと解釈されるだろう。
x = F<A> + y;
この文は,≪型実引数並び≫をもつ≪単純名≫とそれに続く二項加算演算子ではなく,x = (F
< A) > (+y)と書かれているかのように,小なり演算子,大なり演算子及び単項加算演算子と
して解釈されるだろう。
x = y is C<T> + z;
この文における字句C<T>は,≪型実引数並び≫をもつ≪名前空間名又は型名≫として解釈さ
れる。
9.3
字句解析
生成規則≪入力≫では,C#ソースファイルの字句構造を定義する。C#プログラムのソースファイルは,
この字句文法の生成規則に従わなければならない。
≪入力≫::
≪入力節≫opt
≪入力節≫::
≪入力節部≫
≪入力節≫ ≪入力節部≫
≪入力節部≫::
≪入力要素群≫opt ≪改行≫
≪前処理指令≫
72
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪入力要素群≫::
≪入力要素≫
≪入力要素群≫ ≪入力要素≫
≪入力要素≫::
≪空白類≫
≪注釈≫
≪字句≫
C#ソースファイルの字句構造は次の五つの基本要素からなる。すなわち,行終端子(9.3.1参照),空白
類(9.3.3参照),注釈(9.3.2参照),字句(9.4参照)及び前処理指令(9.5参照)とする。この基本要素
のうちでは,字句>が単一の演算子を形成するために別の字句と結合される場合(9.4.5参照)を除くと,
字句だけがC#プログラムの構文文法(9.2.2参照)で重要な働きをする。
C#ソースファイルの字句処理は,ファイルから字句列への変換で構成され,この字句列は構文解析への
入力となる。行終端子,空白類及び注釈は,字句を分割し,前処理指令は,ソースファイルの一部を読み
飛ばすことができるが,それ以外には,これらの字句要素は,C#プログラムの構文構造に影響を及ぼさな
い。
ソースファイルの文字列に複数の字句文法生成規則が一致する場合には,字句処理は,常に,可能なも
ののうちで最も長い字句要素を生成する。
例 例えば,文字列//は,字句要素としては,単一字句/よりも長いので,単一行注釈の先頭として
処理される。
9.3.1
行終端子
行終端子は,C#ソースファイルの文字列を行に分割する。
≪改行≫::
復帰文字 (U+000D)
行送り文字 (U+000A)
復帰文字 (U+000D) と行送り文字 (U+000A) の連なり
次行文字 (U+2085)
行分離文字 (U+2028)
段落分離文字 (U+2029)
EOFマーカを追加するソースコード編集ツールとの互換性を維持するため,及び適切に終端処理された
行の列としてソースファイルを表示できるようにするため,C#プログラムのすべてのソースファイルに対
して次の変換が適用される。
− ソースファイルの最後の文字がCtrl-Z文字 (U+001A) の場合は,この文字を削除する。
− ソースファイルが空でなく,しかも,ソースファイルの末尾の文字が復帰文字 (U+000D),行送り文
字 (U+000A),次行文字 (U+2085),行分離文字 (U+2028) 又は段落分離文字 (U+2029) でない場合
は,ソースファイルの末尾に復帰文字 (U+000D) を追加する。
注記 追加した復帰文字は,終端記号≪改行≫をもたない前処理指令(9.5参照)におけるプログラム
の終止を可能にする。
9.3.2
注釈
注釈には二つの形式,すなわち,区切り注釈及び単一行注釈がある。
区切り注釈は,文字列/*で始まり,文字列*/で終わる。区切り注釈は,行の一部,単一行又は複数行を
73
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
占める。
例
/* Hello, world program
This program writes "hello, world" to the console
*/
class Hello
{
static void Main() {
System.Console.WriteLine("hello, world");
}
}
この例は,区切り注釈を含む。
単一行注釈は,文字列//で始まり,行末で終わる。
例
// Hello, world program
//
This program writes "hello, world" to the console
//
class Hello // any name will do for this class
{
static void Main() { // this method must be named "Main"
System.Console.WriteLine("hello, world");
}
}
この例は,幾つもの単一行注釈を示す。
≪注釈≫::
≪単一行注釈≫
≪区切り注釈≫
≪単一行注釈≫::
// ≪入力文字群≫opt
≪入力文字群≫::
≪入力文字≫
≪入力文字群≫ ≪入力文字≫
≪入力文字≫::
≪改行文字≫以外の任意のUnicode文字
≪改行文字≫::
復帰文字 (U+000D)
行送り文字(U+000A)
次行文字 (U+0085)
行分離文字(U+2028)
段落分離文字(U+2029)
74
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪区切り注釈≫::
/* ≪区切り注釈テキスト≫opt ≪星印群≫/
≪区切り注釈テキスト≫::
≪区切り注釈節≫
≪区切り注釈テキスト≫ ≪区切り注釈節≫
≪区切り注釈節≫::
≪非星印≫
≪星印群≫≪非斜線≫
≪星印群≫::
*
≪星印群≫*
≪非星印≫::
*以外の任意のUnicode文字
≪非斜線≫::
/以外の任意のUnicode文字
注釈は入れ子にはならない。文字列/*及び*/は,単一行注釈の範囲内では,何ら特別な意味をもたない
し,文字列//及び/*は,区切り注釈の範囲内では,何ら特別な意味をもたない。
文字リテラル及び文字列リテラルの中では,注釈はあり得ない。
9.3.3
空白類
空白類文字は,Unicodeの文字クラス属性がZsの文字(これは,空白文字を含む。),水平タブ文字,垂
直タブ文字及び改ページ文字と定義される。
≪空白類≫::
≪空白類文字群≫
≪空白類文字群≫::
≪空白類文字≫
≪空白類文字群≫≪空白類文字≫
≪空白類文字≫::
Unicodeの文字クラスがZsの任意の文字
水平タブ文字 (U+0009)
垂直タブ文字 (U+000B)
改ページ文字 (U+000C)
9.4
字句
字句には幾つかの種類がある。すなわち,識別子,キーワード,リテラル,演算子及び区切り子である。
空白類及び注釈は,字句の分離子として働くが,字句ではない。
≪字句≫::
≪識別子≫
≪キーワード≫
≪整数リテラル≫
≪実数リテラル≫
≪文字リテラル≫
75
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪文字列リテラル≫
≪演算子又は区切り子≫
9.4.1
Unicode逆斜線表記
Unicode逆斜線表記は,Unicode文字を表す。Unicode逆斜線表記は,識別子(9.4.2参照),通常の文字
列リテラル(9.4.4.5参照)及び文字リテラル(9.4.4.4参照)で処理される。これら以外の位置(演算子,
区切り子,キーワードなどを構成する位置など)では,Unicode逆斜線表記の処理はされない。
≪Unicode逆斜線表記≫::
¥u≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字≫
¥U≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字
≫≪16進数字≫≪16進数字≫
Unicode逆斜線表記は,“¥u”又は“¥U”文字に引き続く16進数による単一Unicode文字を表す。C#は,
文字及び文字列値においてUnicode文字の16ビット符号化を用いているので,Unicodeの符号位置が
U+10000 〜 U+10FFFFの範囲にあるものは,二つのUnicodeサロゲート符号単位で表現される。
0x10FFFFより上のUnicode符号位置は,不当であり,使用できない。
2段階以上の変換は行われない。例えば,“¥u005Cu005C”は,“¥”ではなく“¥u005C”と同等とな
る。
注記 Unicode値¥u005Cは,文字“¥”である。
例
class Class1
{
static void Test(bool ¥u0066) {
char c = '¥u0066';
if (¥u0066)
System.Console.WriteLine(c.ToString());
}
}
これは,英字“f”の逆斜線表記である¥u0066の幾つかの使用例を示す。このプログラムは,
次のプログラムと同等となる。
class Class1
{
static void Test(bool f) {
char c = 'f';
if (f)
System.Console.WriteLine(c.ToString());
}
}
9.4.2
識別子
9.4.2で示す識別子の規則は,先頭文字として下線“̲”を使用できること(Cプログラム言語の従来の
規則と同じ。),識別子の中でUnicode逆斜線表記を使用できること,及び“@”文字を接頭辞とすることで
キーワードを識別子として使用できることの3点を除くと,“Unicode Standard Annex #15”で推奨されてい
76
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る規則に厳密に対応する。
≪識別子≫::
≪利用可能識別子≫
@≪識別子又はキーワード≫
≪利用可能識別子≫::
≪キーワード≫でない≪識別子又はキーワード≫
≪識別子又はキーワード≫::
≪識別子開始文字≫≪識別子部分文字群≫opt
≪識別子開始文字≫::
≪C#字≫
̲ (下線 U+005F)
≪識別子部分文字群≫::
≪識別子部分文字≫
≪識別子部分文字群≫≪識別子部分文字≫
≪識別子部分文字≫::
≪C#字≫
≪10進数字文字≫
≪接続文字≫
≪結合文字≫
≪整形文字≫
≪C#字≫::
Unicode の文字クラスがLu, Ll, Lt, Lm, Lo又はNlのUnicode文字
Unicode の文字クラスがLu, Ll, Lt, Lm, Lo又は Nlの文字を表す≪Unicode
逆斜線表記≫
≪結合文字≫::
Unicode の文字クラスがMn又はMcのUnicode文字
Unicode の文字クラスがMn又はMcの文字を表す≪Unicode逆斜線表記≫
≪10進数字文字≫::
Unicode の文字クラスがNd
Unicode の文字クラスがNdの文字を表す≪Unicode逆斜線表記≫
≪接続文字≫::
Unicode の文字クラスがPc
Unicode の文字クラスがPcの文字を表す≪Unicode逆斜線表記≫
≪整形文字≫::
Unicode の文字クラスがCf
Unicode の文字クラスがCfの文字を表す≪Unicode逆斜線表記≫
注記 上で述べられているUnicode文字クラスの情報については,The Unicode Standard,Version 4.0
の4.5を参照。
例 正当な識別子の例には,“identifier1”,“̲identifier2”及び“@if”がある。
適合プログラムでは,“Unicode Standard Annex #15”で定義済みのUnicode正規形Cが規定する標準書式
77
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
の識別子を使用しなければならない。正規形Cを用いていない識別子が検出された場合の振る舞いは,実
装で定義される。ただし,診断を必要とはしない。
接頭辞“@”は,他のプログラム言語とのインタフェースで有用な,キーワードの識別子利用を可能に
する。文字@は実際には識別子の一部ではないので,他の言語では接頭辞のない通常の識別子として認識
される。接頭辞@をもつ識別子は,逐語的識別子と呼ばれる。
注記 キーワードでない識別子での接頭辞@の利用は許されてはいるが,スタイルの点では利用しな
いことを強く推奨する。
例
class @class
{
public static void @static(bool @bool) {
if (@bool)
System.Console.WriteLine("true");
else
System.Console.WriteLine("false");
}
}
class Class1
{
static void M() {
cl¥u0061ss.st¥u0061tic(true);
}
}
この例は,“bool”という名前の仮引数を取る“static”という名前の静的メソッドをもつ
“class”という名前のクラスを定義する。Unicode逆斜線表記は,キーワードでは許されない
ので,字句“cl¥u0061ss”は識別子であり,“@class”と同じ識別子となることに注意する。
次の変換がこの順序で適用された結果が等しい場合,二つの識別子は同一とみなされる。
− 接頭辞“@”が使われているならば,取り除く。
− ≪Unicode逆斜線表記≫を,対応するUnicode文字に変換する。
− ≪整形文字≫を取り除く。
連続した二つの下線文字 (U+005F) を含む識別子は,実装のために予約されているが,そのような識別
子が定義されていないか診断する必要はない。
注記 例えば,実装は,二つの下線で始まる拡張キーワードを提供してもよい。
9.4.3
キーワード
キーワードは,予約された識別子と同様の文字列であり,文字@が頭に付かない限りは,識別子として
用いることはできない。
≪キーワード≫:: 次のいずれか
abstract
as
base
bool
break
byte
case
catch
char
checked
class
const
continue
decimal
default
78
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
delegate
do
double
else
enum
event
explicit
extern
false
finally
fixed
float
for
foreach
goto
if
implicit
in
int
interface
internal
is
lock
long
namespace
new
null
object
operator
out
override
params
private
protected
public
readonly
ref
return
sbyte
sealed
short
sizeof
stackalloc
static
string
struct
switch
this
throw
true
try
typeof
uint
ulong
unchecked
unsafe
ushort
using
virtual
void
volatile
while
識別子add (17.7),alias (16.3),get (17.6.2),global (16.7),partial (17.1.4),remove (17.7),set
(17.6.2),value (17.6.2,17.7.2),where (25.7) 及びyield (15.14) は,構文文法において特別な意味をも
つが,キーワードではない。
利便性及び明確化のために,これらの識別子は構文文法においてはタイプライタ体で表すが,これらは
識別子とする。
注記 結果としてキーワードとは異なるこれらの識別子は,@接頭辞を伴って表記される。また,
Unicode逆斜線表記を含むことができる。
9.4.4
リテラル
リテラル(14.5.1参照)は,値をソースコードとして表現する。
9.4.4.1
真理値リテラル
二つの真理値リテラル値,すなわちtrue及びfalseがある。
≪真理値リテラル≫::
true
false
≪真理値リテラル≫の型は,boolとなる。
9.4.4.2
整数リテラル
整数リテラルは,型int,uint,long及びulongの値を書くために使われる。整数リテラルには,
10進及び16進の二つの形式が可能である。
≪整数リテラル≫::
≪10進整数リテラル≫
≪16進整数リテラル≫
≪10進整数リテラル≫::
≪10進数字群≫≪整数型接尾辞≫opt
≪10進数字群≫::
79
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪10進数字≫
≪10進数字群≫≪10進数字≫
≪10進数字≫:: 次のいずれか
0 1 2 3 4 5 6 7 8 9
≪整数型接尾辞≫:: 次のいずれか
U u L l UL Ul uL ul LU Lu lU lu
≪16進整数リテラル≫::
0x≪16進数字群≫≪整数型接尾辞≫opt
0X≪16進数字群≫≪整数型接尾辞≫opt
≪16進数字群≫::
≪16進数字≫
≪16進数字群≫≪16進数字≫
≪16進数字≫:: 次のいずれか
0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
整数リテラルの型は次で決定される。
− リテラルが接尾辞をもたないならば,その値がint,uint,long,ulongのこの順で最初に表現さ
れた型をとる。
− リテラルの接尾辞が,U又はuならば,その値がuint,ulongのこの順で最初に表現された型をと
る。
− リテラルの接尾辞が,L又はlならば,その値がlong,ulongのこの順で最初に表現された型をと
る。
− リテラルの接尾辞が,UL,Ul,uL,ul,LU,Lu,lU又はluならば,型ulongをとる。
整数リテラルの値がulong型の値域を超えると,コンパイル時エラーになる。
注記 書き方としては,型longのリテラルについて,“l”ではなく“L”を使うことを推奨する。
英字“l”は,数字“1”と間違いやすいからである。
可能なうちで最小のint及びlongの値を10進整数リテラルとして書くために,次の二つの規則があ
る。
− ≪整数型接尾辞≫をもたない,値が2147483648 (231)の≪10進整数リテラル≫が,単項負演算子字句
(14.6.2参照)の直後に続く字句であるときに,(二つの字句の)結果は,型がintで値が−2147483648
(−231)の定数となる。それ以外の状況では,このような≪10進整数リテラル≫はuint型になる。
− ≪整数型接尾辞≫をもたないか≪整数型接尾辞≫が L 又はlで,値が9223372036854775808 (263)の≪
10進整数リテラル≫が単項負演算子字句(14.6.2参照)の直後に続く字句であるときに,(二つの字句
の)結果は,型がlongで値が−9223372036854775808 (−263)の定数となる。それ以外の状況では,こ
のような≪10進整数リテラル≫はulong型になる。
9.4.4.3
実数リテラル
実数リテラルは,型float,double及びdecimalの値を書くために使う。
≪実数リテラル≫::
≪10進数字群≫.≪10進数字群≫≪指数部≫opt≪実数型接尾辞≫opt
.≪10進数字群≫≪指数部≫opt≪実数型接尾辞≫opt
≪10進数字群≫≪指数部≫≪実数型接尾辞≫opt
80
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪10進数字群≫≪実数型接尾辞≫
≪指数部≫::
e≪符号≫opt≪10進数字群≫
E≪符号≫opt≪10進数字群≫
≪符号≫:: 次のいずれか
+ -
≪実数型接尾辞≫:: 次のいずれか
F f D d M m
≪実数型接尾辞≫が指定されないとき,実数リテラルの型は,doubleとする。それ以外の場合は,≪
実数型接尾辞≫が,実数リテラルの型を次のとおりに決める。
− 接尾辞がF又はfの実数リテラルの型はfloatとなる。
例 例えば,リテラル1f,1.5f,1e10f及び123.456Fは,すべて型floatとなる。
− 接尾辞が D 又はd の実数リテラルの型はdoubleとなる。
例 例えば,リテラル1d,1.5d,1e10d及び123.456Dは,すべて型doubleとなる。
− 接尾辞がM又はmの実数リテラルの型はdecimalとなる。
例 例えば,リテラル1m,1.5m,1e10m及び123.456Mは,すべて型decimalとなる。
このリテラルは,正確な値でdecimal値に変換され,必要ならば,銀行型丸め方式(11.1.6参
照)を用いて直近の表現可能値に丸められる。この丸め方式はJIS Z 8401の2. c)規則Aに準じる。
値が丸められない限り,リテラルの小数部けた数は保持される。
注記 したがって,リテラル2.900mは,符号が0,係数が2900,小数部けた数が3のdecimalと
して解析される。
指定されたリテラルが指示された型で表現するには大き過ぎる場合は,コンパイル時エラーになる。
注記 特に,実数リテラルは浮動小数点の無限大を生成することはない。しかしながら,0以外の実
数リテラルは,0に丸められてもよい。
型float又はdoubleの実数リテラルの値は,IEC 60559の“直近への丸め”方式を用いて決定される。
9.4.4.4
文字リテラル
文字リテラルは,単一文字を表し,通常は,'a'のように一重引用符で囲まれた文字からなる。
≪文字リテラル≫::
' ≪文字≫ '
≪文字≫::
≪単一文字≫
≪単純逆斜線表記≫
≪16進逆斜線表記≫
≪Unicode逆斜線表記≫
≪単一文字≫::
任意の文字,ただし' (U+0027), ¥ (U+005C)及び ≪改行文字≫を除く
≪単純逆斜線表記≫:: 次のいずれか
¥' ¥" ¥¥ ¥0 ¥a ¥b ¥f ¥n ¥r ¥t ¥v
≪16進逆斜線表記≫::
¥x≪16進数字≫≪16進数字≫opt≪16進数字≫opt≪16進数字≫opt
81
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
注記 ≪文字≫において,逆斜線文字(¥)の直後の文字は,次のいずれかでなければならない。',
",¥,0,a,b,f,n,r,t,u,U,x,v。そうでなければ,コンパイル時エラーになる。
16進逆斜線表記は,“¥x”の後の16進数の値をもつ単一Unicode文字を表す。
文字リテラルで表現された値がU+FFFFを超える場合は,コンパイル時エラーになる。
文字リテラルにおけるUnicode逆斜線表記(9.4.1参照)は,U+0000からU+FFFFの範囲になければな
らない。
単純逆斜線表記は,次の表に示すように,Unicode文字符号位置を表現する。
逆斜線表記
文字名
Unicode 符号位置
¥'
一重引用符
0x0027
¥"
二重引用符
0x0022
¥¥
逆斜線
0x005C
¥0
ナル
0x0000
¥a
警告
0x0007
¥b
後退
0x0008
¥f
改ページ
0x000C
¥n
改行
0x000A
¥r
復帰
0x000D
¥t
水平タブ
0x0009
¥v
垂直タブ
0x000B
≪文字リテラル≫の型は,charとなる。
9.4.4.5
文字列リテラル
C#では,通常文字列リテラルと逐語的文字列リテラルという2種類の文字列リテラルがある。通常文字
列リテラルは,“hello, world”でのようにゼロ個以上の文字を二重引用符で囲み,(タブ文字¥tのよ
うな)単純逆斜線表記並びに16進及びUnicode逆斜線表記を共に含んでよい。
逐語的文字列リテラルは,一つの文字@の後に,二重引用符,ゼロ個以上の文字,そして閉じる二重引
用符からなる。
例 簡単な例は,@"hello, world"である。
逐語的文字列リテラルでは,一つの二重引用符文字を表現する≪引用符二重表記≫だけを除いて,区切
り符号の間の文字は逐語的に文字どおりに解釈される。特に,逐語的文字列リテラルでは,単純逆斜線表
記並びに16進及びUnicode逆斜線表記は処理されない。逐語的文字列リテラルは,複数行にわたることが
できる。
≪文字列リテラル≫::
≪通常文字列リテラル≫
≪逐語的文字列リテラル≫
≪通常文字列リテラル≫::
" ≪通常文字列リテラル文字群≫opt "
≪通常文字列リテラル文字群≫::
≪通常文字列リテラル文字≫
≪通常文字列リテラル文字群≫ ≪通常文字列リテラル文字≫
≪通常文字列リテラル文字≫::
82
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪単一通常文字列リテラル文字≫
≪単純逆斜線表記≫
≪16進逆斜線表記≫
≪Unicode逆斜線表記≫
≪単一通常文字列リテラル文字≫::
任意の文字,ただし" (U+0022), ¥ (U+005C)及び≪改行文字≫を除く
≪逐語的文字列リテラル≫::
@" ≪逐語的文字列リテラル文字群≫opt "
≪逐語的文字列リテラル文字群≫::
≪逐語的文字列リテラル文字≫
≪逐語的文字列リテラル文字群≫ ≪逐語的文字列リテラル文字≫
≪逐語的文字列リテラル文字≫::
≪単一逐語的文字列リテラル文字≫
≪引用符二重表記≫
≪単一逐語的文字列リテラル文字≫::
"以外の任意の文字
≪引用符二重表記≫::
""
注記 ≪通常文字列リテラル文字≫においては,逆斜線文字(¥)に続くのは,次のいずれかの文字
でなければならない。',",¥,0,a,b,f,n,r,t,u,U,x,v。そうでなければ,コン
パイル時エラーになる。
例
string a = "Happy birthday, Joel";
// Happy birthday, Joel
string b = @"Happy birthday, Joel";
// Happy birthday, Joel
string c = "hello ¥t world";
// hello
world
string d = @"hello ¥t world";
// hello ¥t world
string e = "Joe said ¥"Hello¥" to me";
// Joe said "Hello" to
me
string f = @"Joe said ""Hello"" to me";
// Joe said "Hello" to
me
string g = "¥¥¥¥server¥¥share¥¥file.txt";
//
¥¥server¥share¥file.txt
string h = @"¥¥server¥share¥file.txt";
//
¥¥server¥share¥file.txt
string i = "one¥r¥ntwo¥r¥nthree";
string j = @"one
two
three";
これらは,様々な文字列リテラルの例である。最後の文字列リテラルjは,複数の行にまたが
る逐語的文字列リテラルの例となる。引用符の間にある文字は,改行文字のような空白類も含め
83
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
てそのまま逐語的に保持され,二重引用文字の対は,それぞれ一つの二重引用文字に置き換えら
れる。
注記 16進逆斜線表記は,可変 (1〜4) 個の16進数字を含むことができ,文字列リテラル"¥x123"は,
16進の値で123の単一文字となる。16進値が12の文字の後に文字 3が続く文字列を生成する
には,"¥x00123"又は"¥x12" + "3"と書けばよい。
≪文字列リテラル≫の型は,stringとなる。
文字列リテラルごとに新しい文字列インスタンスが作成されるとは限らない。同じアセンブリにおいて,
文字列等価演算子(14.9.7参照)に従って等価な二つ以上の文字列リテラルがある場合には,これらの文
字列リテラルは,同一の文字列インスタンスを参照する。
例
class Test
{
static void Main() {
object a = "hello";
object b = "hello";
System.Console.WriteLine(a == b);
}
}
二つのリテラルが同じ文字列インスタンスを参照するので,この例の出力は,Trueとなる。
9.4.4.6
nullリテラル
≪nullリテラル≫::
null
≪nullリテラル≫は,null型(11.2.7参照)となる。
9.4.5
演算子及び区切り子
C#には,複数の演算子及び区切り子がある。演算子は,式の中で使われて,演算対象が関係する演算を
示す。
例 式a + bでは,演算子+を二つの演算対象a及びbの加算に用いる。
区切り子は,組にまとめたり分けたりするのに用いる。
≪演算子又は区切り子≫:: 次のいずれか
{
}
[
]
(
)
.
,
:
;
+
-
*
/
%
&
|
^
!
~
=
<
>
?
??
::
++
--
&&
||
->
==
!=
<=
>=
+=
-=
*=
/=
%=
&=
|=
^=
<<
<<=
≪右シフト≫::
> >
84
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪右シフト代入≫::
> >=
≪右シフト≫は,二つの字句>及び>から構成される。同様に,≪右シフト代入≫は,二つの字句>及び
>=から構成される。これらの生成規則群においては,二つの字句の間には,構文文法における他の生成規
則とは異なり,(空白類を含めた)任意の種類の文字を置くことは許されない。
注記 C#に総称を導入する前は,>>及び>>=は,共に単一の字句であった。しかしながら,総称のた
めの文法は,<及び>文字を型仮引数と型実引数とを区切るために用いる。
List<Dictionary<string, int>>のような入れ子になった構築型を使うことがしばしば望
まれる。>及び>を空白によって分離することをプログラマに要求することよりはむしろ,二つ
の≪演算子又は区切り子≫の定義を変えた。
9.5
前処理指令
前処理指令には,ソースファイルの特定部分の条件付き省略,エラー状態及び警告状態の報告,ソース
コードの異なる領域の区切りなどの機能がある。
≪前処理指令≫::
≪前処理宣言≫
≪前処理条件≫
≪前処理行≫
≪前処理診断≫
≪前処理領域≫
≪前処理プラグマ≫
注記 “前処理指令”という用語は,C及びC++プログラム言語との一貫性のためにだけ用いられて
いる。C#では,別立ての前処理は存在しない。前処理指令は,字句解析段階の一部として処理
される。
次のとおり前処理指令が使用できる。
− #define及び#undefは,それぞれ,条件付きコンパイル用記号の定義及びその取消しに用いられる
(9.5.3参照)。
− #if,#elif,#else及び#endifは,ソースコードの一部を条件に応じて読み飛ばすのに用いられ
る(9.5.4参照)。
− #lineは,エラー及び警告において示される行番号の制御に用いられる(9.5.7参照)。
− #error及び#warningは,エラー及び警告を発行するのにそれぞれ用いられる(9.5.5参照)。
− #region及び#endregionは,ソースコードの区画に対し明示的に印を付けるのに用いられる(9.5.6
参照)。
− #pragmaは,コンパイラに対する文脈情報を与えるのに用いられる(9.5.8参照)。
前処理指令は,常に,ソースコードで別々の行に置かれ,常に,文字#とそれに続く前処理指令名で始
まる。文字#の前,及び文字#と前処理指令名との間には,空白類があってもよい。
#define,#undef,#if,#elif,#else,#endif又は#lineという前処理指令を含むソース行は,
単一行注釈で終わってよい。区切り注釈(/* */様式の注釈)は,前処理指令を含むソース行では許され
ない。
前処理指令は,C#の字句でもなければ,構文文法の一部でもない。ただし,前処理指令を使うことで,
字句列を有効にしたり無効にしたりできるので,それによってC#プログラムに影響を与えることができる。
85
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例
#define A
#undef B
class C
{
#if A
void F() {}
#else
void G() {}
#endif
#if B
void H() {}
#else
void I() {}
#endif
}
例えば,このプログラムをコンパイルすると,次のプログラムと全く同じ字句列が生成される。
class C
{
void F() {}
void I() {}
}
したがって,字面上では,この二つのプログラムは全く異なるのに,構文的には同一となる。
9.5.1
条件付きコンパイル用記号
指令#if,指令#elif,指令#else及び指令#endifによって提供される条件付きコンパイル機能は,
前処理式(9.5.2参照)及び条件付きコンパイル用記号によって制御される。
≪条件用記号≫::
≪識別子≫
任意の≪キーワード≫,ただしtrue 及びfalseを除く
条件付きコンパイル用記号には,定義済み及び未定義の二つの状態がある。ソースファイルの字句処理
が始まるときの条件付きコンパイル用記号の状態は,外部機構(コマンド行コンパイラオプションなど)
によって明示的に定義されているのでない限り,未定義となる。指令#defineが処理されると,その指令
に含まれる条件付きコンパイル用記号は,そのソースファイルで定義される。この記号は,その同じ記号
に対する指令#undefが処理されるまで,又は,ソースファイルの末尾に達するまで定義されたままとな
る。この意味は,あるソースファイルにおける指令#define及び指令#undefが同じプログラムであって
も他のソースファイルには影響をもたないことにある。
条件付きコンパイル用記号に対する名前空間は,C#プログラムの他のあらゆる名前付き実体とは別に独
立している。条件付きコンパイル用記号は,指令#define及び指令#undef並びに前処理式においてだけ
参照できる。
86
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
9.5.2
前処理式
前処理式は,指令#if及び指令#elifに使われる。演算子!,==,!=,&&及び||は,前処理式で使う
ことが許されており,更に,まとめのために括弧を使用できる。
≪前処理式≫::
≪空白類≫opt ≪前処理or式≫ ≪空白類≫opt
≪前処理or式≫::
≪前処理and式≫
≪前処理or式≫ ≪空白類≫opt || ≪空白類≫opt ≪前処理and式≫
≪前処理and式≫::
≪前処理等価式≫
≪前処理and式≫ ≪空白類≫opt && ≪空白類≫opt ≪前処理等価式≫
≪前処理等価式≫::
≪前処理単項式≫
≪前処理等価式≫ ≪空白類≫opt == ≪空白類≫opt ≪前処理単項式≫
≪前処理等価式≫ ≪空白類≫opt != ≪空白類≫opt ≪前処理単項式≫
≪前処理単項式≫::
≪前処理一次式≫
! ≪空白類≫opt ≪前処理単項式≫
≪前処理一次式≫::
true
false
≪条件用記号≫
( ≪空白類≫opt ≪前処理式≫ ≪空白類≫opt )
前処理式で参照されるとき,定義済みの条件付きコンパイル用記号は,真理値trueをもち,未定義の
条件付きコンパイル用記号は,真理値falseをもつ。
前処理式を評価すると,結果は常に真理値になる。前処理式の評価規則は,参照可能な利用者定義実体
が条件付きコンパイル用記号に限られるという点を除いては,定数式の評価規則(14.16参照)と同じとす
る。
9.5.3
宣言指令
宣言指令は,条件付きコンパイル用記号を定義済み又は未定義にするために使用する。
≪前処理宣言≫::
≪空白類≫opt # ≪空白類≫opt define ≪空白類≫ ≪条件用記号≫ ≪前処
理改行≫
≪空白類≫opt # ≪空白類≫opt undef ≪空白類≫ ≪条件用記号≫ ≪前処理
改行≫
≪前処理改行≫::
≪空白類≫opt ≪単一行注釈≫opt ≪改行≫
指令#defineの処理によって,与えられた条件付きコンパイル用記号が,ソースファイルでの次の行か
ら定義される。同様に,指令#undefの処理によって,与えられた条件付きコンパイル用記号が,ソース
ファイルでの次の行から未定義となる。
87
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ソースファイルでの指令#define及び指令#undefは,最初の≪字句≫(9.4参照)が出現する前に,
置かなければならない。そうでなければ,コンパイル時エラーになる。直感的には,指令#define及び
#undefは,ソースファイルのいかなる“実際のコード”よりも先になければならない。
例1
#define Enterprise
#if Professional || Enterprise
#define Advanced
#endif
namespace Megacorp.Data
{
#if Advanced
class PivotTable {...}
#endif
}
この例は,指令#defineが,ソースファイルで最初の字句(キーワードnamespace)より
前にあるので,正当となる。
例2 次の例は,指令#defineが実際のコードの後に出てくるので,コンパイル時エラーになる。
#define A
namespace N
{
#define B
#if B
class Class1 {}
#endif
}
指令#defineは,既に定義された条件付きコンパイル用記号を,その記号に対する指令#undefが間に
入っていなくとも定義できる。
例 次の例は,条件付きコンパイル用記号Aを定義し,更に再度定義する。
#define A
#define A
条件付きコンパイル用記号がコンパイラオプションとして定義できるコンパイラにおいては,
このような再定義は,記号をコンパイラオプションとソースとの両方で定義したときに生じる。
指令#undefは,定義されていない条件付きコンパイル用記号を“未定義”にできる。
例 次の例は,条件付きコンパイル用記号A を定義して,その後二度未定義にする。2回目の指令
#undefは,何の効果ももたないが,正当とする。
#define A
#undef A
#undef A
9.5.4
条件付きコンパイル指令
条件付きコンパイル指令は,条件に応じてソースファイルの一部を含めたり,除外したりするのに用い
88
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る。
≪前処理条件≫::
≪前処理if節≫ ≪前処理elif節群≫opt ≪前処理else節≫opt ≪前処理endif
≫
≪前処理if節≫::
≪空白類≫opt # ≪空白類≫opt if ≪空白類≫ ≪前処理式≫ ≪前処理改行≫ ≪条件
節≫opt
≪前処理elif節群≫::
≪前処理elif節≫
≪前処理elif節群≫ ≪前処理elif節≫
≪前処理elif節≫::
≪空白類≫opt # ≪空白類≫opt elif ≪空白類≫ ≪前処理式≫ ≪前処理改行≫ ≪
条件節≫opt
≪前処理else節≫::
≪空白類≫opt # ≪空白類≫opt else ≪前処理改行≫ ≪条件節≫opt
≪前処理endif≫::
≪空白類≫opt # ≪空白類≫opt endif ≪前処理改行≫
≪条件節≫::
≪入力節≫
≪読み飛ばし節≫
≪読み飛ばし節≫::
≪読み飛ばし節部≫
≪読み飛ばし部≫ ≪読み飛ばし節部≫
≪読み飛ばし節部≫::
≪空白類≫opt ≪読み飛ばし文字群≫opt ≪改行≫
≪前処理指令≫
≪読み飛ばし文字群≫::
≪非#≫ ≪入力文字群≫opt
≪非#≫::
# 以外の任意の≪入力文字≫
注記 構文が示すように,条件付きコンパイル指令は,指令#if,ゼロ個以上の指令#elif,高々1
個の指令#else及び指令#endifをこの順序で書かなければならない。ソースコードのこれら
の指令の間には,条件節が来る。条件部は,直前の指令によって制御される。条件部は,完結
した条件付きコンパイル指令を入れ子として含むことができる。
≪前処理条件≫では,含まれている≪条件節≫の中の高々一つを,通常の字句処理のために選択する。
− 指令#if及び指令#elifの≪前処理式≫は,どれかがtrueになるまで順に評価される。式がtrue
と評価された場合は,対応する指令の≪条件節≫が選択される。
− すべての≪前処理式≫がfalseとなり,指令#elseが存在するならば,指令#elseの≪条件節≫が
選択される。
− それ以外の場合は,どの≪条件節≫も選択されない。
89
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪条件節≫が選択されると,その部分は通常の≪入力節≫として処理される。すなわち,その部分に含
まれるソースコードは,字句文法に準拠している必要があり,その部分のソースコードから字句が生成さ
れて,その部分内の前処理指令は規定どおりに機能する。
≪条件節≫が残っていた場合,その部分は≪読み飛ばし節≫として処理される。すなわち,前処理指令
を除き,セクション内のソースコードは字句文法に準拠している必要はなく,その部分のソースコードか
らは,字句が生成されず,その部分内の前処理指令は,字句的に正しい必要があるが,それ以外の処理は
されない。≪読み飛ばし節≫として処理される≪条件節≫の内部では,(入れ子になった構成要素
#if...#endif及び#region...#endregionに含まれる)≪条件節≫も,≪読み飛ばし節≫として処理さ
れる。
例 次の例は,条件付きコンパイル指令が,どのように入れ子になるかを示す。
#define Debug
// Debugging on
#undef Trace
// Tracing off
class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#if Trace
WriteToLog(this.ToString());
#endif
#endif
CommitHelper();
}
…
}
前処理指令を除き,読み飛ばされるソースコードは字句解析の対象とならない。例えば,次の
例は,#elseの部分に閉じのない注釈があるにもかかわらず正当となる。
#define Debug
// Debugging on
class PurchaseTransaction
{
void Commit() {
#if Debug
CheckConsistency();
#else
/* Do something else
#endif
}
…
}
ただし,前処理指令は,ソースコードの読み飛ばされる節であっても字句的に正しく記述され
90
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ている必要があることに注意する。
複数行入力要素の内部にある前処理指令は,処理されない。次に示すプログラムは,その例で
ある。
class Hello
{
static void Main() {
System.Console.WriteLine(@"hello,
#if Debug
world
#else
Nebraska
#endif
");
}
}
出力は次となる。
hello,
#if Debug
world
#else
Nebraska
#endif
特殊な場合として,処理される前処理指令の集合が,≪前処理式≫の評価に依存する場合があ
る。
#if X
/*
#else
/* */ class Q { }
#endif
この例では,Xが定義されているかいないかにかかわらず,同じ字句ストリーム(class Q { })
が生成される。もしも,Xが定義されているならば,複数行注釈によって,処理される指令は,
#if及び#endifだけとなる。もしも,Xが未定義ならば,三つ(#if,#else,#endif)が指
令に含まれる。
9.5.5
診断指令
診断指令は,エラー メッセージや警告メッセージを明示的に生成するために使用する。このようなメッ
セージは,他のコンパイル時エラーや警告と同じように通知される。
≪前処理診断≫::
≪空白類≫opt # ≪空白類≫opt error ≪前処理メッセージ≫
≪空白類≫opt # ≪空白類≫opt warning ≪前処理メッセージ≫
≪前処理メッセージ≫::
91
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪改行≫
≪空白類≫ ≪入力文字群≫opt ≪改行≫
例
#warning Code review needed before check-in
#if Debug && Retail
#error A build can't be both debug and retail
#endif
class Test {…}
この例は,常に(“Code review needed before check-in”)という警告を発し,もしも,
前処理識別子Debug及びRetailが共に定義されていると,コンパイル時エラーにもなる。≪
前処理メッセージ≫が任意のテキストを保持できることに注意する。具体的には,語canʼtに一
重引用符が含まれているように,整形式の字句である必要がない。
9.5.6
領域制御
領域指令は,ソースコードの領域を明示的に印付けるのに用いる。
≪前処理領域≫::
≪前処理領域開始≫ ≪条件節≫opt ≪前処理領域終了≫
≪前処理領域開始≫::
≪空白類≫opt # ≪空白類≫opt region ≪前処理メッセージ≫
≪前処理領域終了≫::
≪空白類≫opt # ≪空白類≫opt endregion ≪前処理メッセージ≫
領域に特別な意味が付加されることはない。領域は,プログラマ又は自動化ツールがソースコードに特
定の部分を作成するときに使用する。指令#region又は指令#endregionで指定されるメッセージも特
別な意味はない。領域を特定するためだけに使われる。釣り合った指令#region及び指令#endregion
が異なる≪前処理メッセージ≫をもつことができる。
次のような領域の字句処理について述べる。
#region
...
#endregion
すなわち,この領域指令は,次の形式の条件付きコンパイル指令の字句処理と全く同じとする。
#if true
...
#endif
9.5.7
行指令
行指令を使うと,警告又はエラーなどのコンパイラ出力で報告される行番号及びソースファイル名を変
更できる。
≪前処理行≫::
≪空白類≫opt # ≪空白類≫opt line ≪空白類≫ ≪行表示子≫ ≪前処理改行
≫
≪行表示子≫::
≪10進数字群≫ ≪空白類≫ ≪ファイル名≫
92
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪10進数字群≫
≪識別子又はキーワード≫
≪ファイル名≫::
" ≪ファイル名文字群≫ "
≪ファイル名文字群≫::
≪ファイル名文字≫
≪ファイル名文字群≫≪ファイル名文字≫
≪ファイル名文字≫::
任意の文字,ただし" (U+0022) 及び ≪改行文字≫を除く
注記 行指令は,他のテキスト入力からC#ソースコードを生成するメタプログラミングツールで使わ
れるのが最も一般的である。
指令#lineがないときには,コンパイラは,出力として真の行番号及びファイル名を通知する。≪識別
子又はキーワード≫でない≪行表示子≫を含む指令#lineを処理すると,コンパイラは,指令の次の行を
あたかも与えられた行番号(及び,指定されていればファイル名)をもつかのように扱う。
≪行表示子≫が(9.4.2で規定されている等価性を用いて)defaultと同じ値をもつ≪識別子又はキー
ワード≫となる場合の指令#lineは,それまでのすべての先行した#line指令の効果を破棄する。コンパ
イラは,あたかもこれまで一つも#line指令が処理されなかったかのように,引き続く行に対して真の行
情報を通知する。
defaultでない値をもつ≪識別子又はキーワード≫を用いた≪行表示子≫の目的は,実装によって定義
される。≪行表示子≫におけるそのような≪識別子又はキーワード≫を認識しない実装は,警告を発行し
なければならない。
注記 ≪ファイル名≫は,逆斜線表記が処理されないという点で通常文字列リテラルと異なることに
注意する。文字ʻ¥ʼは,≪ファイル名≫の中では,ただ単に通常の逆斜線文字を表すにすぎない。
9.5.8
プラグマ指令
#pragma指令は,コンパイラに文脈情報を指定するために用いられる前処理指令とする。
注記 例えば,コンパイラは次のような#pragma指令を与える。
− 続くコードをコンパイルするときに特定の警告メッセージを許可したり禁止したりする。
− 続くコードに適用する最適化をどれにするか指定する。
− デバッガによって使われる情報を指定する。
≪前処理プラグマ≫::
≪空白類≫opt # ≪空白類≫opt pragma ≪前処理プラグマテキスト≫
≪前処理プラグマテキスト≫::
≪改行≫
≪空白類≫ ≪入力文字群≫opt ≪改行≫
≪前処理プラグマテキスト≫での≪入力文字群≫は,実装で定義される方法にのっとってコンパイラに
よって解釈される。#pragma指令で供給される情報は,プログラムの意味を変えてはならない。#pragma
指令は,この規格の範囲外であるコンパイラの振る舞いを変えることだけにしなければならない。コンパ
イラが≪入力文字群≫を解釈できない場合,コンパイラは警告を生成することができるが,コンパイル時
エラーを生成してはならない。
93
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
注記 ≪前処理プラグマテキスト≫は,任意のテキストを保持できる。具体的には,整形式の字句を
含む必要がない。
10 基本概念
10.1 アプリケーション開始
アプリケーション開始は,実行環境が指定メソッドを呼び出したとき起きる。このメソッドは,アプリ
ケーションの入口点という。この入口点メソッドは,常にMainと名前付けられ,次の呼出し情報のいず
れか一つをもつ。
static void Main() {…}
static void Main(string[] args) {…}
static int Main() {…}
static int Main(string[] args) {…}
この例のように,入口点は,int型の値を返すことができる。この返却値は,アプリケーションの終了
で使用される(10.2参照)。
入口点は,一つの仮引数を取ることができる。この仮引数は,どのような名前にもできる。そのような
引数を宣言するときには,次の二つの制約に従わなければならない。
− 実装は,この引数の値がnullでないことを保証する。
− 引数名をargsと仮定する。argsで指定された配列の長さがゼロより大きければ,args[0]から
args[args.Length-1]までの配列要素は,アプリケーション引数と呼ばれる文字列を参照しなけれ
ばならない。このアプリケーション引数は,アプリケーション開始に先立ってホスト環境から実装定
義の値を与えられる。この意図は,アプリケーション開始前にホスト環境のどこかで決定されたアプ
リケーション情報を供給するためとする。ホスト環境に,大文字と小文字との両方の文字による文字
列を供給する機能がなければ,実装は,文字列が小文字で受け取られることを保証する。
注記 コマンド行が使えるシステムでは,アプリケーション引数は,一般にコマンド行引数と呼
ばれるものに対応する。
C#ではメソッドの多重定義が使えるため,呼出し情報が異なってさえいれば,クラス又は構造体は,メ
ソッドの複数の定義を保持できる。ただし,単一のプログラム内では,クラス又は構造体は,アプリケー
ションの入口点として使用されるように定義されたMainという名前のメソッドを二つ以上含むことはで
きない。ただし,引数が複数ある場合,又は引数が一つだけでもstring[]以外の型である場合は,Main
を多重定義したメソッドがあってもかまわない。
アプリケーションは,複数のクラス又は構造体で構成できる。その定義がアプリケーション入口点とし
ての使用に合格するMainと呼ばれるメソッドは,複数のクラス又は構造体に含まれることができる。そ
の場合には,アプリケーション開始が起きるようにこれらのMainメソッドから一つが選択されなければ
ならない。入口点のこの選択処理は,この規格の範囲を超えるので,入口点を指定したり決定する機構に
ついては述べない。
C#では,すべてのメソッドは,クラス又は構造体のメンバとして定義されなければならない。普通,メ
ソッドの宣言されたアクセス可能性(10.5.1参照)は,メソッドの宣言で指定されたアクセス修飾子(17.2.3
参照)によって決まり,同様に,型の宣言されたアクセス可能性は,型の宣言で指定されたアクセス修飾
子によって決まる。ある型のあるメソッドが呼出し可能であるためには,型及びメソッドの両方がアクセ
ス可能でなければならない。ただし,アプリケーションの入口点は特殊な場合となる。宣言されたアクセ
94
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ス可能性にも,取り囲む型宣言の宣言されたアクセス可能性にもかかわりなく,実行環境はアプリケーシ
ョンの入口点にアクセスできる。
入口点のメソッドは,総称クラス宣言(25.1参照)又は総称構造体宣言(25.2参照)で定義されてはな
らない。
それ以外の点に関しては,入口点メソッドも,入口点ではないメソッドと同じように振る舞う。
10.2 アプリケーション終了
アプリケーションの終了時は,実行環境に制御が戻る。
アプリケーションの入口点メソッドの返却値の型がintの場合,返される値はアプリケーションの終了
状態コードになる。このコードを利用することで,プログラムの成功又は失敗を実行環境に通知できる。
入口点メソッドの返却値の型がvoidの場合に,メソッドを終了させる右波括弧(})に到達するか,又
は式のないreturn文を実行すると,終了状態コードが0になる。
アプリケーションの終了前に,ゴミ集めが行われていないすべてのオブジェクトに対して終了化子が呼
び出される。ただし,ライブラリメソッドGC.SuppressFinalizeなどの呼出しによってアプリケーシ
ョンの後始末が抑止されていない場合に限る。
10.3 宣言
C#プログラムにおける宣言は,プログラムの構成要素を定義する。C#プログラムは,名前空間宣言(箇
条16参照)を使って構成されており,名前空間は,型の宣言及び入れ子になった名前空間宣言を含むこと
ができる。型宣言(16.6参照)は,クラス(箇条17参照),構造体(箇条18参照),インタフェース(箇
条20参照),列挙(箇条21参照)及び委譲(箇条22参照)を定義するのに使われる。型宣言に許される
メンバの種類は,型宣言の形式に依存する。例えば,クラス定義は,定数(17.3参照),フィールド(17.4
参照),メソッド(17.5参照),特性(17.6参照),イベント(17.7参照),添字子(17.8参照),演算子(17.9
参照),インスタンス構築子(17.10参照),終了化子(17.12参照),静的構築子(17.11参照)及び入れ子
型を含むことができる。
宣言では,宣言が属する宣言空間の名前を定義する。複数の宣言で,一つの宣言空間に同じ名前のメン
バを導入するとコンパイル時エラーになる。ただし,次の場合を除く。
− 同じ名前をもつ二つ以上の名前空間宣言は,同じ宣言空間内で許可される。このような名前空間宣言
は,単一の論理名前空間に集約され単一の宣言空間を共有する。
− 異なるプログラムだが同じ名前空間の宣言空間内での宣言は,同じ名前を共有することが許される。
− 同じ名前だが異なる呼出し情報をもつ二つ以上のメソッドは,同じ宣言空間にあることを許される
(10.6参照)。
− 同じ名前だが異なる数の型仮引数をもつ二つ以上の型宣言は,同じ宣言空間にあることを許される
(10.8.2参照)。
− 同じ宣言空間内のpartial修飾子をもつ二つ以上の型宣言は,同じ名前,同じ数の型仮引数,及び
同じ分類(class,struct又はinterface)を共有してもよい。この場合,型宣言は,単一の型に
寄与し,それら自身を単一の宣言空間に集約する(17.1.4参照)。
− 同じ宣言空間にある名前空間宣言及び型宣言は,その型宣言が少なくとも一つの型仮引数をもつなら
ば,同じ名前を共有することができる(10.8.2参照)。
型宣言空間が,同じ名前をもった異なる種類のメンバを含むことはできない。
例 一つの型宣言空間に同じ名前のフィールド及びメソッドを含むことはできない。
宣言空間の型には,次のとおりの種類がある。
95
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− プログラムのすべてのソースファイルにおいて,取り囲む≪名前空間宣言≫がない≪名前空間メンバ
宣言≫ は,大域的宣言空間と呼ばれる単一の組み合わされた宣言空間のメンバとする。
− プログラムのすべてのソースファイルにおいて,同一の完全限定名前空間名をもつ≪名前空間宣言≫
内の≪名前空間メンバ宣言≫は,単一の組み合わされた宣言空間のメンバとする。
− 各≪コンパイル単位≫及び≪名前空間本体≫は,別名宣言空間をもつ。≪コンパイル単位≫又は≪名
前空間本体≫の各≪外部別名指令≫及び≪using別名指令≫は,別名宣言空間へメンバを寄与する
(16.4.1参照)。
− 部分型でないクラス宣言,構造体宣言又はインタフェース宣言は,新しい宣言空間を作る。部分クラ
ス,部分構造体又は部分インタフェース宣言は,すべての同じプログラム中で合致する部分によって
共有される宣言空間へ寄与する。名前は,≪型仮引数並び≫,≪クラスメンバ宣言≫,≪構造体メン
バ宣言≫又は≪インタフェースメンバ宣言≫を通して,この宣言空間に導入される。多重定義された
インスタンス構築子宣言及び多重定義された静的構築子宣言を除き,クラスメンバ又は構造体メンバ
の宣言は,クラス又は構造体と同じ名前でメンバを導入することはできない。クラス,構造体又はイ
ンタフェースでは,メソッド及び添字子を多重定義宣言できる。さらに,クラス又は構造体では,イ
ンスタンス構築子,演算子及び型を多重定義宣言できる。
例 クラス,構造体又はインタフェースは,それぞれのメソッド宣言が呼出し情報で異なるなら
ば,同一名の複数のメソッド宣言を含むことができる(10.6参照)。クラス又は構造体は,型
仮引数の数が異なる型によって与えられる名前と同じ名前をもつ複数の入れ子型を含むこと
ができる。
基底クラスは,クラスの宣言空間に寄与しないし,基底インタフェースは,インタフェースの宣言
空間に寄与しない。したがって,派生クラス又は派生インタフェースでは,継承されたメンバと同じ
名前でメンバを宣言できる。
− 各列挙型宣言では,新しい宣言空間が作成される。名前は,≪列挙メンバ宣言≫を通してこの宣言空
間に導入される。
− 各≪ブロック≫,≪switchブロック≫,≪for文≫,≪foreach文≫又は≪using文≫は,局所
変数宣言空間と呼ばれる局所変数及び局所定数のための宣言空間を作成する。名前は,≪局所変数宣
言≫及び≪局所定数宣言≫を通じて,この宣言空間に導入される。ブロックがインスタンス構築子,
メソッド若しくは演算子の宣言の本体である場合,又は添字子宣言のgetアクセス子若しくはset
アクセス子である場合,それらの宣言内で宣言される仮引数は,ブロックの局所変数宣言空間のメン
バとする。ブロックが総称メソッドの本体の場合,その総称メソッド宣言で宣言された型仮引数は,
そのブロックの局所変数宣言空間のメンバとする。局所変数宣言空間の二つのメンバが同じ名前をも
つことはエラーとする。局所変数宣言空間及び入れ子になった局所変数宣言空間が同じ名前の要素を
含むことはエラーとする。
注記 このように入れ子になったブロックの中では,外側の局所変数又は局所定数と同じ名前をもっ
た,局所変数又は局所定数を宣言することはできない。二つの入れ子になったブロックにおい
て,一方が他方を含んでいない限り,その二つのブロックで同じ名前をもった要素を含むこと
はできる。
− 各≪ブロック≫又は≪switchブロック≫は,ブロックのラベル宣言空間と呼ばれるラベルのための
宣言空間を別途作成する。それらの名前は,≪ラベル付き文≫を通じて,この宣言空間に導入され,
goto文を通じて参照される。ブロックのラベル宣言空間及び入れ子になったブロックのラベル宣言
96
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
空間が同じ名前の要素を含むことはエラーとする。したがって,入れ子になっているブロックの中で,
外側のブロックのラベルと同じ名前のラベルを宣言することはできない。
注記 二つの入れ子になったブロックにおいて,一方が他方を含んでいない限り,その二つのブロッ
クで同じ名前をもった要素を含むことができる。
名前を宣言する際に,記述の順序は一般的に重要ではない。特に,名前空間,定数,メソッド,特性,
イベント,添字子,演算子,インスタンス構築子,終了化子,静的構築子及び型を宣言したり使用したり
する場合,記述の順序は重要ではない。宣言の順序は,次の場合に重要となる。
− フィールド宣言及び局所変数の場合は,宣言の順序によって,初期化子(存在する場合)の実行順序
が決まる。同じ型のための複数の部分型宣言のフィールド宣言がある場合,その部分の順序は不定と
する。しかし,それぞれのフィールド初期化子部分の中は順番に実行される。
− 局所変数及び局所定数は,それらが使われる前に定義されなければならない(10.7参照)。
− ≪定数式≫値が省略される場合,列挙メンバ宣言(21.3参照)での宣言順序は重大となる。
例 名前空間の宣言空間には,“限度がない”ので,同一の完全限定名の二つの名前空間は,同一の宣
言空間に寄与する。次に,二つの名前空間の宣言が,同一の宣言空間に寄与する例を示す。
namespace Megacorp.Data
{
class Customer
{
…
}
}
namespace Megacorp.Data
{
class Order
{
…
}
}
この場合,二つのクラスを,完全限定名Megacorp.Data.Customer及び
Megacorp.Data.Orderとして宣言する。二つの宣言は,同一の宣言空間に寄与するため,同
一の名前のクラスの宣言を各々が含む場合は,コンパイル時エラーになる。
すべての部分型の宣言は,同じ宣言空間に寄与する。
partial class C
{
int F;
partial struct N1 {…}
partial class N2 {…}
partial class N3 {…}
}
partial class C
97
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
void F() {…}
// Error: conflicts with field F
partial class N1 {…} // Error: conflicts with struct N1
class N2 {…}
// Error: conflicts with other N2
partial class N3 {…} // Ok
}
Cのための二つの部分宣言は,単一の宣言空間及び単一の型を形成して組み合わせる。一番目の部分に
あるFと名付けられたフィールドは,二番目の部分にあるFという名前のメソッドと衝突する。一番目の
部分にあるN1と名付けられた構造体は,二番目にあるN1と名付けられたクラスに衝突する。二番目の部
分にある部分クラスではないN2は,一番目にある部分クラスN2と衝突する。二つのN3のための部分宣
言は,単一の型C.N3を形成して組み合わせる。
注記 上で規定したとおり,一つのブロックの宣言空間は,任意の入れ子になったブロックの宣言空
間の名前を共有することはできない。したがって,次の例にある,Fメソッド 及びGメソッド
は,名前iが,外側のブロックの中に宣言され,内側のブロックの中に再宣言できないので,
結果としてコンパイル時エラーになる。ただし,Hメソッド及びIメソッドは,二つのiが,
別々の入れ子になっていないブロックの中で宣言されているので,正当とする。
class A
{
void F() {
int i = 0;
if (true) {
int i = 1;
}
}
void G() {
if (true) {
int i = 0;
}
int i = 1;
}
void H() {
if (true) {
int i = 0;
}
if (true) {
int i = 1;
}
}
void I() {
for (int i = 0; i < 10; i++)
98
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
H();
for (int i = 0; i < 10; i++)
H();
}
}
10.4 メンバ
名前空間及び型は,メンバをもつ。
注記 実体のメンバは,一般に,実体への参照から始めて字句“.”,その後にメンバ名という限定名
を使うことができる。
ある型のメンバは,その型の中で宣言するか,又はその型の基底クラスから継承する。基底クラスから
型を継承すると,インスタンス構築子,終了化子及び静的構築子を除く,基底クラスのすべてのメンバは,
派生型のメンバになる。基底クラスのメンバに宣言されたアクセス可能性が,メンバの継承の可否を制御
することはない。インスタンス構築子,静的構築子又は終了化子以外のすべてのメンバが,継承の対象に
なる。ただし,継承されたメンバは,宣言したアクセス可能性(10.5.1参照)のため,又は派生型での宣
言によって隠ぺい(10.7.1.2参照)されたため,派生型においてアクセス可能でないことがある。
10.4.1 名前空間のメンバ
取り囲む名前空間をもたない名前空間及び型は,大域的名前空間のメンバとなる。これは,大域的宣言
空間で宣言されている名前での場合に直接対応する。
名前空間の中で宣言されている名前空間及び型は,その外側の名前空間のメンバとする。これは,名前
空間の宣言空間で宣言されている名前での場合に直接対応する。
名前空間には,アクセス制限はない。名前空間を非公開,限定公開又は内部と宣言することはできず,
名前空間の名前は常に公開アクセス可能とする。
10.4.2 構造体のメンバ
構造体のメンバは,構造体の中で宣言されたメンバ及び構造体の直接基底クラスSystem.ValueType
及び間接基底クラスobjectから継承するメンバとする。
単純型のメンバは,単純型によって別名付けされた構造体型のメンバとする(11.1.4参照)。
10.4.3 列挙のメンバ
列挙のメンバは,列挙の中で宣言された定数及び列挙の直接基底クラスSystem.Enum並びに間接基底
クラスSystem.ValueType及びobjectのメンバとする。
10.4.4 クラスのメンバ
クラスのメンバは,そのクラスの中で宣言されているメンバ及び基底クラスから継承されたメンバとす
る(ただし,クラスobjectには基底クラスがないので例外とする。)。定数,フィールド,メソッド,特
性,イベント,添字子,演算子,及び型であるメンバは基底クラスから継承されるが,インスタンス構築
子,終了化子及び静的構築子は,基底クラスから継承されない。基底クラスのメンバは,アクセス可能性
に関係なく継承される。
クラスの宣言には,定数,フィールド,メソッド,特性,イベント,添字子,演算子,インスタンス構
築子,終了化子,静的構築子及び型の宣言を含めることができる。
object及びstringのメンバは,それらの別名のクラス型のメンバに直接対応する。
− objectのメンバは,System.Objectクラスのメンバとする。
− stringのメンバは,System.Stringクラスのメンバとする。
99
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
10.4.5 インタフェースのメンバ
インタフェースのメンバは,そのインタフェースの中で宣言されたメンバ及びそのインタフェースのす
べての基底インタフェースの中で宣言されたメンバとする。
注記 クラスobjectの中のメンバは,厳密にいえば,任意のインタフェースのメンバではない(20.2
参照)。ただし,クラスobjectの中のメンバは,任意のインタフェース型の中でメンバの検
索を経由して利用可能とする(14.3参照)。
10.4.6 配列のメンバ
配列のメンバは,System.Arrayクラスから継承されたメンバとする。
10.4.7 委譲のメンバ
委譲のメンバは,System.Delegateクラスから継承されたメンバとする。
10.5 メンバアクセス
メンバの宣言は,メンバのアクセス制御を許可する。メンバのアクセス可能性は,メンバの宣言された
アクセス可能性(10.5.1参照)に,このメンバを直接含む型のアクセス可能性を組み合わせて確立される。
特定のメンバへのアクセスが許可されている場合,そのメンバをアクセス可能なメンバと呼ぶ。反対に,
特定のメンバへのアクセスが許可されていない場合,そのメンバをアクセス不可能なメンバと呼ぶ。メン
バへのアクセスは,アクセスが行われるテキストの位置が,メンバのアクセス可能領域(10.5.2参照)に
含まれる場合に許可される。
10.5.1 宣言されたアクセス可能性
メンバの宣言されたアクセス可能性は,次のいずれかとする。
− 公開 (public) は,メンバの宣言の中にpublic修飾子を含むことによって選択される。直感的な
publicの意味は,“アクセスが制限されていない”とする。
− 限定公開 (protected) は,メンバの宣言の中にprotected修飾子を含むことによって選択される。
直感的なprotectedの意味は,“アクセスは,含んでいるクラス又は含んでいるクラスから派生され
る型に制限される。”とする。
− 内部 (internal) は,メンバの宣言の中にinternal修飾子を含むことによって選択される。直感
的なinternalの意味は,“アクセスは,このプログラムの中に制限される。”とする。
− 限定公開内部 (protected internal) は,メンバの宣言の中にprotected修飾子及びinternal
修飾子の両方を含むことによって選択される。直感的なprotected internalの意味は,“アクセス
は,このプログラム,又は含んでいるクラスから派生される型に制限される。”とする。
− 非公開 (private) は,メンバの宣言の中にprivate修飾子を含むことによって選択される。直感
的なprivateの意味は,“アクセスは含んでいる型に制限される。”とする。
メンバの宣言が行われる文脈によっては,特定の種類の宣言されたアクセス可能性しか許されない。メ
ンバの宣言にアクセス修飾子が全く含まれていない場合は,宣言が行われる文脈に基づいて,省略時の宣
言されたアクセス可能性が決まる。
− 名前空間は,暗黙にpublicと宣言されたアクセス可能性をもつ。名前空間の宣言では,アクセス修
飾子を指定できない。
− コンパイル単位又は名前空間での型は,public又はinternalと宣言されたアクセス可能性しかも
てず,省略時はinternalと宣言されたアクセス可能性をもつ。
− クラスのメンバは,任意の5種類の宣言されたアクセス可能性をもち,省略時はprivateを宣言さ
れたアクセス可能性とする。
100
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
注記 クラスのメンバとして宣言された型は,任意の5種類の宣言されたアクセス可能性をもつ
ことができるのに対し,名前空間のメンバとして宣言された型は,public又はinternal
の宣言されたアクセス可能性しかもてない。
− 構造体のメンバはpublic,internal又はprivateと宣言されたアクセス可能性をもつことがで
きるが,構造体は暗黙に封印されているので省略時はprivate と宣言されたアクセス可能性をもつ。
構造体の中に導入された(すなわち,構造体によって継承していない)構造体のメンバは,protected
又はprotected internalと宣言されたアクセス可能性をもつことはできない。
注記 構造体のメンバとして宣言された型は,public,internal又はprivateの宣言された
アクセス可能性をもつことができるのに対し,名前空間のメンバとして宣言された型は,
public又はinternalの宣言されたアクセス可能性しかもてない。
− インタフェースのメンバは,publicと宣言されたアクセス可能性を暗黙にもつ。インタフェースの
メンバ宣言に対しては,アクセス修飾子を指定できない。
− 列挙のメンバは,publicと宣言されたアクセス可能性を暗黙にもつ。列挙のメンバの宣言に対して
は,アクセス修飾子を指定できない。
10.5.2 アクセス可能領域
メンバのアクセス可能領域とは,そのメンバへのアクセス可能性が与えられた,プログラムテキストの
(互いに離れていることもある)一部分とする。メンバのアクセス可能領域を定義する目的のために,メ
ンバが,型の内部で宣言されていない場合,最上位と呼び,型の内部で宣言されている場合,入れ子にな
っていると呼ぶ。さらに,プログラムのテキストとは,そのプログラムのすべてのソースファイル中に含
まれるすべてのソーステキストとして定義され,また,ある型のソーステキストとは,その型のためのす
べての宣言≪クラス本体≫,≪構造体本体≫,≪インタフェース本体≫,又は,≪列挙本体≫の中の字句
開き“{”と字句閉じ“}”との間に含まれる(複数の部分宣言及びその型の中で入れ子になっているすべ
ての型を含む可能性もある。)すべてのソーステキストとして定義される。
(object,int又はdoubleのような)あらかじめ定義された型のアクセス可能領域は,無制限とす
る。
プログラムPで宣言された最上位型Tのアクセス可能領域は,次のとおり定義される。
− Tの宣言されたアクセス可能性がpublicの場合,Tのアクセス可能領域は,プログラムテキストP
及びPを参照する任意のプログラムとする。
− Tの宣言されたアクセス可能性がinternalの場合,Tのアクセス可能領域は,Pのプログラムテキ
ストとする。
注記 これらの定義から,最上位の型のアクセス可能領域は,少なくともその型が宣言されているプ
ログラムのプログラムテキストとなることが分かる。
プログラムP内での型Tの中で定義された入れ子になったメンバMのアクセス可能領域は,次のとおり
定義される(M自体が型であってもよいことに注意する。)。
− Mの宣言されたアクセス可能性がpublicの場合,Mのアクセス可能領域は,Tのアクセス可能領域
とする。
− Mの宣言されたアクセス可能性がprotected internalの場合,DをPのプログラムテキストと,P
の外側で宣言された,Tから派生した任意の型のプログラムテキストとの和集合とする。Mのアクセ
ス可能領域は,Tのアクセス可能領域とDとの共通部分とする。
− Mの宣言されたアクセス可能性がprotectedの場合,DをTのプログラムテキストとTから派生し
101
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
た任意の型のプログラムテキストとの和集合とする。Mのアクセス可能領域は,Tのアクセス可能領
域とDとの共通部分とする。
− Mの宣言されたアクセス可能性がinternalの場合,Mのアクセス可能領域は,Tのアクセス可能領
域とPのプログラムテキストとの共通部分とする。
− Mの宣言されたアクセス可能性がprivateの場合,Mのアクセス可能領域は,Tのプログラムテキス
トとする。
注記 上の定義から,入れ子になったメンバのアクセス可能領域は,常に少なくとも,そのメンバが
宣言されている型のプログラムテキストとなることが分かる。さらに,メンバのアクセス可能
領域は,メンバが宣言された型のアクセス可能領域よりも広いことはない。
注記 直感的には,型又はメンバ Mがアクセスされた場合,アクセス許可の保証は,次の手順で評価
される。
− 最初に,Mが,コンパイル単位又は名前空間でなく,型の中で宣言されているときは,その型自体が
アクセス可能でないとコンパイル時エラーになる。
− 次に,Mがpublicの場合,アクセスは許可される。
− そうでなく,Mがprotected internalの場合,Mが宣言されたプログラム内にあるときは,アク
セスが許可される。又は,Mが宣言されたクラスから派生したクラスにあり,派生したクラス型(10.5.3
参照)を通じて行われたとき,アクセスは許可される。
− そうでなく,Mがprotectedの場合には,Mが宣言されたクラスの中にあるとき,アクセスは許可
される。又は,Mが宣言されたクラスから派生したクラスの中にあり,派生したクラス型(10.5.3参
照)を通じて行われたときは,アクセスは許可される。
− そうでなく,Mがinternalの場合,Mが宣言されたプログラム内にあるとき,アクセスは許可され
る。
− そうでなく,Mがprivateの場合,Mが宣言された型の中にあるとき,アクセスは許可される。
− そうでない場合,この型又はメンバは,アクセス可能ではなく,コンパイル時エラーになる。
例
public class A
{
public static int X;
internal static int Y;
private static int Z;
}
internal class B
{
public static int X;
internal static int Y;
private static int Z;
public class C
{
public static int X;
102
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
internal static int Y;
private static int Z;
}
private class D
{
public static int X;
internal static int Y;
private static int Z;
}
}
このコードのクラス及びメンバのアクセス可能領域は,次のとおりとなる。
・ A及びA.Xのアクセス可能領域は,無制限となる。
・ A.Y,B,B.X,B.Y,B.C,B.C.X及びB.C.Yのアクセス可能領域は,含まれるプログラムの
プログラムテキストとなる。
・ A.Zのアクセス可能領域は,Aのプログラムテキストとなる。
・ B.Z及びB.Dのアクセス可能領域は,B.C及びB.D のプログラムテキストを含む,Bのプロ
グラムテキストとなる。
・ B.C.Zのアクセス可能領域は,B.Cのプログラムテキストとなる。
・ B.D.X及びB.D.Yのアクセス可能領域は,B.C及びB.Dのプログラムテキストを含む,Bの
プログラムテキストとなる。
・ B.D.Zのアクセス可能領域は,B.Dのプログラムテキストとなる。
この例に示したように,メンバのアクセス可能領域が,それを含む型のアクセス可能領域を超
えることはない。例えば,どのメンバXも公開と宣言されたアクセス可能性をもつが,A.Xを除
いては,どのメンバXのアクセス可能領域も,それを含む型のアクセス可能領域に制約される。
10.4で規定するように,インスタンス構築子,終了化子及び静的構築子以外の,すべての基底クラスの
メンバは,派生型によって継承される。これには,基底クラスの非公開メンバも含まれる。ただし,非公
開メンバのアクセス可能領域は,メンバが宣言されている型のプログラムテキストだけとなる。
例
class A
{
int x;
static void F(B b) {
b.x = 1;
// Ok
}
}
class B: A
{
static void F(B b) {
b.x = 1;
// Error, x not accessible
}
103
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
このコードでは,クラスBはクラスAから非公開メンバxを継承する。このメンバは非公開な
ので,Aの≪クラス本体≫内でだけアクセス可能とする。したがって,b.xへのアクセスは,メ
ソッドA.Fの中では成功するが,メソッドB.Fの中では失敗する。
10.5.3 インスタンスメンバのための限定公開アクセス
protectedのインスタンスメンバに,そのメンバが宣言されているクラスのプログラムテキストの外
側からアクセスする場合,及び,protected internalのインスタンスメンバに,そのメンバが宣言さ
れているプログラムのプログラムテキストの外側からアクセスする場合は,アクセスを行う派生クラス型
のインスタンスを通してアクセスする必要がある。Bは限定公開インスタンスメンバMを宣言する基底ク
ラスとし,DはBから派生するクラスとする。Dの≪クラス本体≫の中で,Mへのアクセスは,次の形式
の一つをとることができる。
− 形式Mの非限定の≪型名≫又は≪一次式≫。
− Eの型がD又はDから派生するクラスならば,形式E.Mの≪一次式≫。
− 形式 base.Mの≪一次式≫。
これらのアクセスの形式に加えて,派生クラスは,≪構築子初期化子≫(17.10.1参照)内の基底クラス
の限定公開インスタンス構築子をアクセスできる。
例
public class A
{
protected int x;
static void F(A a, B b) {
a.x = 1;
// Ok
b.x = 1;
// Ok
}
}
public class B: A
{
static void F(A a, B b) {
a.x = 1;
// Error, must access through instance of B
b.x = 1;
// Ok
}
}
この例では,Aの中においては,A及びBの両方のインスタンスを通したxへのアクセスがで
きる。なぜなら,いずれの場合も,アクセスはAのインスタンス又はAから派生するクラスのイ
ンスタンスを通じて行われるからである。ただし,Bの中においては,AはBから派生していな
いので,Aのインスタンスを通じてxをアクセスすることはできない。
総称(25.1.6参照)の文脈では,protected 及びprotected internalであるインスタンスメンバ
をアクセスする規則は,次のように増補される。
− 総称クラスG内では,形式E.Mの≪一次式≫を使う継承された限定公開のインスタンスメンバMへの
アクセスは,Eの型が Gから構築されたクラス型,又はGから構築されたクラス型から派生したクラ
104
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ス型とする場合に許可される。
10.5.4 アクセス可能性制約
C# 言語の幾つかの構築要素においては,その型が,メンバや他の型と少なくとも同程度にアクセス可
能の必要がある。型Tのアクセス可能領域が,メンバ又は型Mのアクセス可能領域を集合として含む場合,
Tは,Mと少なくとも同程度にアクセス可能という。言い換えれば,Mがアクセス可能なすべての文脈に
おいて,Tがアクセス可能ならば,Tは,Mと少なくとも同程度にアクセス可能となる。
アクセス可能性には,次のとおりの制約がある。
− クラス型の直接基底クラスは,少なくとも,そのクラス型自体と同程度にアクセス可能でなければな
らない。
− インタフェース型の明示的な基底インタフェースは,そのインタフェース型と少なくとも同程度にア
クセス可能でなければならない。
− 委譲型の返却値の型及び仮引数の型は,その委譲型自体と少なくとも同程度にアクセス可能でなけれ
ばならない。
− 定数の型は,定数自体と少なくとも同程度にアクセス可能でなければならない。
− フィールドの型は,フィールド自体と少なくとも同程度にアクセス可能でなければならない。
− メソッドの返却値の型及び仮引数の型は,メソッド自体と少なくとも同程度にアクセス可能でなけれ
ばならない。
− 特性の型は,特性自体と少なくとも同程度にアクセス可能でなければならない。
− イベントの型は,イベント自体と少なくとも同程度にアクセス可能でなければならない。
− 添字子の型及び仮引数の型は,添字子自体と少なくとも同程度にアクセス可能でなければならない。
− 演算子の返却値の型及び仮引数の型は,演算子自体と少なくとも同程度にアクセス可能でなければな
らない。
− インスタンス構築子の仮引数の型は,インスタンス構築子自体と少なくとも同程度にアクセス可能で
なければならない。
例
class A {…}
public class B: A {…}
このコードでは,AがBと同程度のアクセス可能性をもたないため,クラスBは,コンパイル
時エラーになる。
例
class A {…}
public class B
{
A F() {…}
internal A G() {…}
public A H() {…}
}
同様に,上のコードでは,Bの中のメソッドHは,返却値の型Aが少なくともメソッドHと同
程度のアクセス可能性をもたないため,コンパイル時エラーになる。
105
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
10.6 呼出し情報及び多重定義
メソッド,インスタンス構築子,添字子及び演算子は,その呼出し情報によって特徴付けられる。
− メソッドの呼出し情報は,メソッド名,型仮引数の数,並びに,各仮引数の型及び種類(値,参照又
は出力)の左からの並びで構成される。メソッドの呼出し情報には,返却値の型,仮引数名又は型仮
引数名を含まず,右端の仮引数のためのparams修飾子も含まない。仮引数の型がメソッドの型仮引
数を含む場合,型仮引数の通常の位置は型仮引数の名前ではなく,型の同値性を用いる。
− インスタンス構築子の呼出し情報は,各仮引数の型及び種類(値,参照又は出力)の左からの並びで
構成される。インスタンス構築子の呼出し情報には,仮引数名又は右端の仮引数のためのparams修
飾子は含まない。
− 添字子の呼出し情報は,各仮引数の型の左からの並びで構成される。添字子の呼出し情報には,要素
型又は仮引数名も含まないし,右端の仮引数のためのparams修飾子も含まない。
− 演算子の呼出し情報は,演算子名及び各仮引数の型の左からの並びで構成される。演算子の呼出し情
報には,結果の型又は仮引数名を含まない。
呼出し情報は,クラス,構造体及びインタフェースのメンバの多重定義のための機構を実現する。
− メソッドの多重定義を利用すると,クラス,構造体又はインタフェースで,呼出し情報が一意であれ
ば,名前の同じ複数のメソッドを宣言できる。
− インスタンス構築子の多重定義を利用すると,クラス又は構造体で,呼出し情報が一意であれば,複
数のインスタンス構築子を宣言できる。
− 添字子の多重定義を利用すると,クラス,構造体又はインタフェースで,呼出し情報が一意であれば,
複数の添字子を宣言できる。
− 演算子の多重定義を利用すると,クラス又は構造体で,呼出し情報が一意であれば,複数の演算子を
宣言できる。
out仮引数修飾子及びref仮引数修飾子は,呼出し情報の一部と考えられるにもかかわらず,単一の型
で宣言されたメンバでは,refとoutだけの相違では,呼出し情報は区別されない。このメソッドの中の
すべてのout修飾子の仮引数をref修飾子に変えると同一の呼出し情報になるような同一の型の二つの
メンバが宣言された場合は,コンパイル時エラーになる。(例えば,隠ぺい又は多重定義など)このメソッ
ド呼出し情報の合致以外においては,ref及びoutは,呼出し情報の一部となり,互いに一致しない。
注記 この制限は,C#プログラムを共通言語基盤 (CLI) 上で実行するコンパイルを簡単にする。CLI
では,refとoutだけが違うメソッドを定義する方法がない。
例 次の例は,多重定義されたメソッド宣言と呼出し情報との対を示す。
interface ITest
{
void F();
// F()
void F(int x);
// F(int)
void F(ref int x);
// F(ref int)
void F(out int x);
// F(out int) error
void F(int x, int y);
// F(int, int)
int F(string s);
// F(string)
int F(int x);
// F(int) error
void F(string[] a);
// F(string[])
106
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void F(params string[] a);
// F(string[]) error
void F<S>(S s);
// F<̀0>(̀0)
void F<T>(T t);
// F<̀0>(̀0) error
void F<S,T>(S s);
// F<̀0,̀1>(̀0)
void F<T,S>(S s);
// F<̀0,̀1>(̀1) ok
}
ref及びoutの仮引数修飾子(17.5.1参照)は,呼出し情報の一部であることに注意する。し
たがって,F(int),F(ref int)及びF(out int)は,すべて一意な呼出し情報となる。ただし,
F(ref int)及びF(out int) の呼出し情報はrefとoutの差だけしかないので,これらを同
一のインタフェース内で宣言することはできない。また,返却値の型及びparams修飾子は呼出
し情報に含まれないことに注意する。したがって,返却値の型の差異だけに基づいて,又は
params修飾子指定の有無だけに基づいて,多重定義を行うことはできない。上の例での,メソ
ッドF(int)及びメソッドF(params string[])の宣言は,コンパイル時エラーになる。
10.7 有効範囲
名前の有効範囲とは,名前に対して限定を必要とせずに,その名前で宣言された実体を参照できるプロ
グラムテキスト領域を指す。有効範囲は入れ子にでき,内側の有効範囲では,外側の有効範囲にある名前
の意味を再宣言できる。
注記 これは,ただし,10.3で課せられた,入れ子になったブロック内で,取り囲むブロックの局所
変数と同一の名前をもった局所変数又は局所定数を宣言することはできないという制約を除外
しない。
外側の有効範囲の名前は,内側の有効範囲のプログラムテキストの領域で隠ぺいされる。外側の名前へ
のアクセスは,名前の限定によってだけ可能となる。
− 取り囲む≪名前空間宣言≫がない≪名前空間メンバ宣言≫(16.5参照)によって宣言された名前空間
メンバの有効範囲は,プログラムテキスト全体となる。
− 完全限定名がNの≪名前空間宣言≫の中で≪名前空間メンバ宣言≫によって宣言された名前空間メン
バの有効範囲は,N又はNで始まり一つのピリオドが続くような完全限定名のすべての≪名前空間宣
言≫の≪名前空間本体≫とする。
− ≪外部別名指令≫(16.3参照)によって定義される名前の範囲は,その≪外部別名指令≫がある≪コ
ンパイル単位≫又は≪名前空間本体≫の≪using指令≫,≪大域的属性≫及び≪名前空間メンバ宣言
≫にまで及ぶ。≪外部別名指令≫は,基礎とする宣言空間に対していかなる新しいメンバも寄与しな
い。言い換えれば,≪外部別名指令≫は,推移的ではない。しかし,正しくは≪外部別名指令≫が記
述されている≪コンパイル単位≫又は≪名前空間本体≫にだけで有効とする。
− ≪using指令≫(16.4参照)によって定義又は移入される名前の有効範囲は,≪using指令≫が記述
されている≪大域的属性≫及び≪コンパイル単位≫又は≪名前空間本体≫の≪名前空間メンバ宣言≫
にまで及ぶ。≪using指令≫は,特定の≪コンパイル単位≫又は≪名前空間本体≫の中で利用できる
0個以上の名前空間名又は型名を作成できるが,基礎とする宣言空間に新規メンバを追加することは
ない。すなわち,≪using指令≫は推移的ではなく,≪using指令≫が記述されている≪コンパイル
単位≫及び≪名前空間本体≫の中だけで有効とする。
− ≪クラスメンバ宣言≫(17.2参照)で宣言されるメンバの有効範囲は,その宣言がある≪クラス本体
≫とする。加えて,クラスメンバの有効範囲は,そのメンバのアクセス可能領域(10.5.2参照)に含
107
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
まれる派生クラスの≪クラス本体≫まで広げられる。
− ≪構造体メンバ宣言≫(18.2参照)で宣言されるメンバの有効範囲は,その宣言がある≪構造体本体
≫とする。
− ≪列挙メンバ宣言≫(21.3参照)で宣言されるメンバの有効範囲は,その宣言がある≪列挙本体≫と
する。
− ≪メソッド宣言≫(17.5参照)内で宣言される仮引数の有効範囲は,≪メソッド宣言≫の≪メソッド
本体≫とする。
− ≪添字子宣言≫(17.8参照)内で宣言される仮引数の有効範囲は,≪添字子宣言≫の≪アクセス子宣
言群≫とする。
− ≪演算子宣言≫(17.9参照)内で宣言される仮引数の有効範囲は,≪演算子宣言≫の≪ブロック≫と
する。
− ≪構築子宣言≫(17.10参照)内で宣言される仮引数の有効範囲は,≪構築子初期化子≫及びその≪構
築子宣言≫の≪ブロック≫とする。
− ≪ラベル付き文≫(15.4参照)内で宣言されるラベルの有効範囲は,その宣言のある≪ブロック≫と
する。
− ≪局所変数宣言≫(15.5.1参照)内で宣言された局所変数の有効範囲は,その宣言がある≪ブロック
≫とする。
− switch文の≪switchブロック≫(15.7.2参照)内で宣言された局所変数の有効範囲は,その≪
switchブロック≫とする。
− for文の≪for初期化子≫(15.8.3参照)内で宣言された局所変数の有効範囲は,≪for初期化子≫,
≪for条件≫,≪for反復子≫及びそのfor文内の≪文≫とする。
− ≪局所定数宣言≫(15.5.2参照)内で宣言された局所定数の有効範囲は,その宣言がある≪ブロック
≫とする。≪定数宣言子≫より前の記述位置で局所定数を参照すると,コンパイル時エラーになる。
名前空間,クラス,構造体又は列挙のメンバの有効範囲では,メンバの宣言より前の記述位置でそのメ
ンバを参照できる。
例
class A
{
void F() {
i = 1;
}
int i = 0;
}
この例のように,iが宣言されるより前に,iを参照するFは正当とする。
局所変数の有効範囲では,局所変数の≪局所変数宣言子≫より前の記述位置で,その局所変数を参照す
るとコンパイル時エラーになる。
例
class A
{
int i = 0;
108
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void F() {
i = 1;
// Error, use precedes
declaration
int i;
i = 2;
}
void G() {
int j = (j = 1);
// Valid
}
void H() {
int a = 1, b = ++a;
// Valid
}
}
上のメソッドFにおいて,最初のiへの代入は,外側のクラスAでの有効範囲で宣言されたフ
ィールドを参照しない。それは,局所変数を参照しており,その記述位置が変数の宣言より前に
あるので,コンパイル時エラーになる。メソッドGにおいては,jの宣言のための初期化子の中
でのjの使用は,正当となる。なぜなら,そのi使用が,≪局所変数宣言子≫より前にないから
である。メソッドHにおいては,二つ目の≪局所変数宣言子≫は,同一の≪局所変数宣言≫の中
で,一つ目の≪局所変数宣言子≫内で宣言された局所変数aを正しく参照する。
注記 局所変数及び局所定数に対する有効範囲の規則は,式の文脈で使われる名前の意味がブロック
内で常に同じであることを保証するために設けられている。局所変数の有効範囲を,その宣言
からブロックの終わりまでとしてしまうと,上の例では,最初のiへの代入がインスタンス変
数への代入であり,二番目のiへの代入が,局所変数への代入になってしまうことになる(上
の例とは異なる状況においては,ブロック文を後で再配置した場合には,コンパイル時エラー
になる。)。
ブロック内の名前の意味は,名前が使用される文脈によって異なる。次に例を示す。
例
using System;
class A {}
class Test
{
static void Main() {
string A = "hello, world";
// declarator context
string s = A;
// expression context
Type t = typeof(A);
// type context
Console.WriteLine(s);
// writes "hello,
world"
Console.WriteLine(t.ToString());
// writes "Type: A"
109
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
名前Aは,局所変数Aへの参照という式の文脈及びクラスAへの参照という型の文脈の中で
使用される。
10.7.1 名前の隠ぺい
一般に,実体の有効範囲は,実体の宣言空間より大きいプログラムテキストを含んでいる。特に,実体
の有効範囲には,同じ名前の実体を含む新しい宣言空間を導入する宣言を含むことができる。このような
宣言は,元の実体を隠ぺいする。逆に,隠ぺいされていない実体は可視という。
入れ子及び継承によって有効範囲が重複すると,名前の隠ぺいが発生する。この2種類の隠ぺいの特徴
を,10.7.1.1及び10.7.1.2で規定する。
局所変数,局所定数,仮引数及びメソッド型仮引数は,他の局所変数,局所定数,仮引数又はメソッド
型仮引数(10.3参照)を隠ぺいできない。
10.7.1.1 入れ子による隠ぺい
入れ子による名前の隠ぺいは,名前空間内で名前空間又は型を入れ子にした場合,クラス又は構造体の
中で型を入れ子にした場合,及び,仮引数や局所変数及び局所定数を宣言した場合に,発生する可能性が
ある。
例
class A
{
int i = 0;
void F() {
int i = 1;
}
void G() {
i = 1;
}
}
この例において,メソッドF内では,クラスAのインスタンス変数iが局所変数iによって隠
ぺいされる,しかし,メソッドG内では,iは依然としてインスタンス変数を参照する。
内側の有効範囲内の名前が外側の有効範囲内の名前を隠ぺいする場合は,その名前をもつ多重定義され
たすべてが隠ぺいされる。
例
class Outer
{
static void F(int i) {}
static void F(string s) {}
class Inner
{
void G() {
F(1);
// Invokes Outer.Inner.F
110
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
F("Hello");
// Error
}
static void F(long l) {}
}
}
この例において,呼出しF(1)は,外側のすべてのFが,内側の宣言によって隠ぺいされるた
め,Innerの中で宣言されたFを呼び出す。同じ理由で,呼出しF("Hello")は,コンパイル
時エラーになる。
10.7.1.2 継承による隠ぺい
クラス又は構造体が基底クラスから継承された名前を宣言し直すと,継承による名前の隠ぺいが発生す
る。継承による名前の隠ぺいは,次のいずれかの形式で行われる。
− 定数,フィールド,特性,又はイベントをクラス又は構造体に導入すると,型仮引数のない,同じ名
前をもつ基底クラスのすべてのメンバが隠ぺいされる。
− クラス又は構造体に導入された型は,同じ名前で同じ数の型仮引数をもつ基底クラスのすべてのメン
バを隠ぺいする。
− クラス又は構造体にメソッドを導入すると,基底クラスのメソッド以外のメンバで,同じ名前であっ
て,型仮引数が存在しないことを含め,型仮引数の数が同じであり,同じ呼出し情報をもつ基底クラ
スのすべてのメソッドが隠ぺいされる。
− クラス又は構造体に添字子を導入すると,同じ呼出し情報(仮引数の個数及び型)をもつ基底クラス
のすべての添字子が隠ぺいされる。
外部有効範囲からの名前の隠ぺいとは異なり,継承された有効範囲にあるアクセス可能な名前を隠ぺい
すると,警告が報告される。
例
class Base
{
public void F() {}
}
class Derived: Base
{
public void F() {}
// Warning, hiding an inherited name
}
Derived内のFの宣言は,警告を報告する。継承された名前の隠ぺいはエラーではない。こ
れをエラーにすると,基底クラスの独立な進化ができなくなる。例えば,上の状況は,Baseの
後の版が,以前のクラスの版に存在していなかったメソッドFを導入した場合に起こり得る。こ
のような状況をエラーにしてしまうと,独立して版管理されているクラスライブラリの基底クラ
スに対して何らかの変更を行うことによって,派生クラスが無効になる可能性がある。
継承された名前を隠ぺいすることによって発生する警告は,new修飾子の使用によって除去できる。
例
class Base
{
111
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public void F() {}
}
class Derived: Base
{
new public void F() {}
}
修飾子newは,Derivedの中のFが“新しいもの”であること,及び,継承されたメンバを
隠ぺいすることを確かに意図することを表す。
修飾子newを指定したメンバの宣言は,そのメンバの有効範囲内だけで継承されたメンバを隠ぺいする。
例
class Base
{
public static void F() {}
}
class Derived: Base
{
new private static void F() {}
// Hides Base.F in Derived only
}
class MoreDerived: Derived
{
static void G() { F(); }
// Invokes Base.F
}
上の例では,Derivedの中のFの宣言は,Baseから継承されるFを隠ぺいするが,Derived
の中の修飾子newを指定したFは,非公開のアクセスをもつので,その有効範囲はMoreDerived
へ拡張しない。したがって,MoreDerived.Gの中の呼出しF()は正当で,Base.Fを呼び出す。
10.8 名前空間及び型名
C#プログラムの幾つかの文脈は,一つの≪名前空間名≫又は≪型名≫を指定する必要がある。
≪名前空間名≫:
≪名前空間名又は型名≫
≪型名≫:
≪名前空間名又は型名≫
≪名前空間名又は型名≫:
≪識別子≫ ≪型実引数並び≫opt
≪限定別名メンバ≫
≪名前空間名又は型名≫ . ≪識別子≫ ≪型実引数並び≫opt
≪名前空間名≫は,名前空間を参照する≪名前空間名又は型名≫とする。≪名前空間名≫の≪名前空間
名又は型名≫は,次で説明する解決規則に従って名前空間を参照する必要がある。この規則に従わないと,
コンパイル時エラーになる。型実引数(25.5.1参照)は,≪名前空間名≫に現れるべきではない(型だけ
が型実引数をもつ。)。
≪型名≫は,型名を参照する≪名前空間名又は型名≫とする。≪型名≫の≪名前空間名又は型名≫は,
112
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
次で説明する解決規則に従って型を参照する必要がある。この規則に従わないと,コンパイル時エラーに
なる。
≪限定別名メンバ≫の構文及び意味は,16.7で定義される。
≪限定別名メンバ≫でない≪名前空間名又は型名≫は,次の四つの形式のどれか一つをもつ。
− I
− I<A1, ..., AK>
− N.I
− N.I<A1, ..., AK>
ここで,Iは,単一の識別子で,Nは≪名前空間名又は型名≫,<A1, ..., AK>は選択的な≪型実引数並び
≫とする。≪型実引数並び≫が指定されていない場合,Kはゼロとみなす。
≪名前空間名又は型名≫の意味は,次の規則に従って決定される。
− ≪名前空間名又は型名≫が≪限定別名メンバ≫の場合,その意味は,16.7で規定される。
− それ以外の場合で,≪名前空間名又は型名≫がIの形式又はI<A1, ..., AK>の形式をとる場合
・ Kがゼロ,かつ,≪名前空間名又は型名≫が総称メソッド宣言(25.6参照)内に現れ,かつ,その
宣言が名前Iをもつ型仮引数(25.1.1参照)を含む場合,≪名前空間名又は型名≫は,その型仮引
数を参照する。
・ それ以外の場合で,≪名前空間名又は型名≫が型宣言の本体内に現れる場合,型Tの型宣言のイン
スタンス型から始まり,各囲みクラス又は構造体宣言のインスタンス型が存在する限り,各インス
タンス型T(25.1.2参照)に対して,
− Kがゼロ,かつ,名前Iをもつ型仮引数を含むTの宣言の場合,≪名前空間名又は型名≫はその
型仮引数を参照する。
− それ以外の場合で,名前Iをもつ入れ子になったアクセス可能型及び型仮引数Kを含むTの場合,
≪名前空間名又は型名≫は,与えられた型実引数を構築する型を参照する。一つ以上このような
型がある場合,より派生した型の中で宣言された型が選択される。
注記 型メンバでない(定数,フィールド,メソッド,特性,添字子,演算子,インスタン
ス構築子,終了化子,及び静的構築子)及び型仮引数の数が異なる型メンバは,≪名
前空間名又は型名≫の意味が決定されるときに無視される。
・ それ以外の場合は,各名前空間Nに対して,≪名前空間名又は型名≫が存在する名前空間から開始
し,外側にある各名前空間(存在する限り)を経て,大域的名前空間に至るまで,実体を見つける
まで次の手順を評価する。
− Kがゼロ,かつ,IがNにおける名前空間の名前の場合
・ ≪名前空間名又は型名≫がある場所がNの名前空間宣言によって囲まれていて,その名前空間
宣言が,名前空間又は型がIという名前で関連付けられている≪外部別名指令≫又は≪using
別名指令≫を含む場合,≪名前空間名又は型名≫は,あいまいで,コンパイル時エラーになる。
・ そうでない場合は,≪名前空間名又は型名≫はNにおけるIと名付けられた名前空間を参照す
る。
− そうでない場合で,Nが,Iという名前をもつアクセス可能型及び型仮引数Kを包含する場合
・ ≪名前空間名又は型名≫がある場所がNの名前空間宣言によって囲まれていて,その名前空間
宣言が,名前空間又は型がIという名前で関連付けられている≪外部別名指令≫又は≪using
別名指令≫を包含する場合,≪名前空間名又は型名≫は,あいまいで,コンパイル時エラーに
113
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
なる。
・ そうでない場合,≪名前空間名又は型名≫は与えられた型実引数で構築された型を参照する。
− そうでない場合で,≪名前空間名又は型名≫がある場所は,Nの名前空間宣言によって囲まれて
いる場合
・ Kがゼロで移入された名前空間又は型がIという名前で関連付けられている≪外部別名指令≫
又は≪using別名指令≫を包含する場合,≪名前空間名又は型名≫は,その名前空間又は型を
参照する。
・ そうでない場合で,名前空間宣言の≪using名前空間指令≫によって移入された名前空間が,
Iという名前及び型仮引数Kをもつ唯一の型を包含する場合,≪名前空間名又は型名≫は,与
えられた型実引数で構築された型を参照する。
・ そうでない場合で,名前空間宣言の≪using名前空間指令≫によって移入された名前空間が,
一つ以上のIという名前及び型仮引数Kをもつ型を包含する場合,≪名前空間名又は型名≫は,
あいまいでエラーになる。
・ そうでない場合,≪名前空間名又は型名≫は未定義で,コンパイルエラーになる。
− そうでない場合で,≪名前空間名又は型名≫がN.Iの形式又はN.I<A1, ..., AK>の形式の場合,N
は,≪名前空間名又は型名≫としてまず解決される。Nの解決が成功しなかった場合,コンパイル時
エラーになる。さもなければ,≪名前空間名又は型名≫がN.Iの形式又はN.I<A1, ..., AK>の形
式は,次のように解決される。
・ KがゼロでNが名前空間を参照し,Nが名前がIの入れ子になった名前空間を包含する場合,≪名
前空間名又は型名≫は,入れ子になった名前空間を参照する。
・ そうでない場合,Nが名前空間を参照し,かつ,NがIという名前をもつアクセス可能型及び型仮
引数K を包含する場合,≪名前空間名又は型名≫は,与えられた型実引数で構築された型を参照す
る。
・ そうでない場合,Nが(おそらく構築)クラス又は構築型を参照し,かつ,NがIという名前をも
つ入れ子になったアクセス可能型及び型仮引数Kを包含する場合,≪名前空間名又は型名≫は,与
えられた型実引数で構築された型を参照する。そのような型が一つ以上ある場合,より派生した型
の中で宣言された型を選択する。
・ そうでない場合,N.Iは,不当な≪名前空間名又は型名≫とし,コンパイル時エラーになる。
≪名前空間名又は型名≫が静的クラスを参照することを許されるのは,次の場合となる。
− ≪名前空間名又は型名≫が,T.Iという形式の≪名前空間名又は型名≫におけるTの場合,又は,
− ≪名前空間名又は型名≫が,typeof(T)の形式の≪typeof式≫(14.5.11参照)におけるTの場合
10.8.1 非限定名
すべての名前空間宣言及び型宣言は,次のように決定される非限定名をもつ。
− 名前空間宣言に関して,非限定名はその宣言の中で指定された≪限定付き識別子≫とする。
− ≪型仮引数並び≫のない型宣言に関して,非限定名はその宣言の中で指定された≪識別子≫とする。
− 型仮引数Kをもつ型宣言に関して,非限定名はその宣言の中で指定された≪識別子≫とし,そして≪
総称次元指定子≫(14.5.11参照)が続く。
10.8.2 完全限定名
すべての名前空間宣言及び型宣言は,完全限定名をもつ。完全限定名は,特定の名前空間又は型を一意
に示す。非限定名Nをもつ名前空間宣言又は型宣言の完全限定名は次のとおり決定される。
114
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 宣言がコンパイル単位に直接含んでおらず,他の宣言のいずれにも入れ子になっていない場合,その
完全限定名はNとする。
− それ以外の場合,その完全限定名はS.Nとする。ここで,Sは直接囲み名前空間又は型宣言の完全限
定名とする。
言い換えれば,宣言の完全限定名は,大域的名前空間から始まり,型又は名前空間へ導く識別子[及び,
≪総称次元指定子≫(14.5.11参照)]の完全に階層的なパスとする。宣言の完全限定名は,その宣言に関
連する名前空間,総称型でないもの又は総称インスタンス型(25.1.2参照)を一意に識別しなければなら
ない。同じ完全限定名が,二つの異なる実体を参照した場合,コンパイル時エラーとする。特に,次のよ
うに規定する。
− 名前空間宣言及び型宣言がいずれも同じ完全限定名をもつことはエラーとする。
− 二つの異なる種類の型宣言が同じ完全限定名をもつことはエラーとする(例えば,構造体とクラス宣
言とが両方とも同じ完全限定名をもつ場合。)。
− partial修飾子なしの型宣言が,他の型宣言として同じ完全限定名をもつことは,エラーとする
(17.1.4参照)。
例 名前空間及び型の宣言及びその完全限定名を示す。
class A {}
// A
namespace X
// X
{
class B
// X.B
{
class C {}
// X.B.C
}
namespace Y
// X.Y
{
class D {}
// X.Y.D
}
}
namespace X.Y
// X.Y
{
class E {}
// X.Y.E
class G<T> {
// X.Y.G<>
class H {}
// X.Y.G<>.H
}
class G<S,T> {
// X.Y.G<,>
class H<U> {}
// X.Y.G<,>.H<>
}
}
10.9 自動メモリ管理機能
C#では自動メモリ管理機能が採用されており,開発者は,オブジェクトが占有するメモリの割当て及び
解放を手動で行う必要はない。ゴミ集め子が自動メモリ管理機能の方針を実装する。オブジェクトのメモ
115
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
リ管理の全段階は次となる。
1) オブジェクトが生成されると,メモリが割り当てられ,構築子が実行され,オブジェクトが有効で
あるとみなされる。
2) オブジェクトのどの一部も,終了化子実行を除き,あらゆる実行継続中でもアクセスできない場合,
そのオブジェクトは使用されていないとみなされ,終了化できる状態になる。
注記 実装は,オブジェクトへのどの参照が将来使用されるか決定するためにコード分析を選択
してもよい。例えば,有効範囲内にある局所変数が,オブジェクトへの唯一存在する参照
であり,その局所変数が,手続中の現在の実行点からあらゆる可能な実行継続において,
参照されることが決してない場合,実装は,そのオブジェクトをもはや使用されていない
と取り扱ってもよい[しかし,必す(須)とはしない。]。
3) いったん,オブジェクトが終了化できる資格があるとされると,適切な時間後に,そのオブジェク
トのための終了化子(17.12参照)が(存在する場合)実行される。明示的な呼出しによって上書き
されない限り,そのオブジェクトの終了化子が実行されるのは1回だけとする。
4) オブジェクトに対して終了化子が実行されると,オブジェクト又はその一部が,終了化子の実行も
含め,どのような方法で実行を継続してもアクセスできない場合,そのオブジェクトはアクセス不
可能とみなされて,ゴミ集めできる状態になる。
5) 最後に,オブジェクトがゴミ集めの対象になってから適切な時間後に,ゴミ集め子がそのオブジェ
クトに関連付けられたメモリを解放する。
ゴミ集め子は,オブジェクトの使用情報を保持する。この情報を用いて,新しく生成されたオブジェク
トをメモリ内で配置する位置,オブジェクトを再配置する時期,オブジェクトが使用されなくなる時期,
すなわちアクセス不可能な時期の決定など,メモリ管理に関する決定を行う。
ゴミ集め子の存在を前提としている他の言語と同じように,C#も,ゴミ集め子が広範なメモリ管理方針
を実装できるように設計されている。例えば,C#では,オブジェクトが終了化やゴミ集めの対象になって
も,終了化子の実行やゴミ集めをすぐには要求しない。また,終了化子が特定の順序で実行されることや,
特定のスレッドで実行されることなども要求しない。
ゴミ集め子の振る舞いは,ある程度は,クラスSystem.GCのメソッドを通じて,制御可能とする。
例 ゴミ集め子では,オブジェクトのごみ集め時期や終了化子の実行時期の決定をかなり自由に行う
ことができるため,規格適合処理系でも,次のコードで示されているものとは結果が異なる場合
がある。
using System;
class A
{
~A() {
Console.WriteLine("Finalize instance of A");
}
}
class B
{
object Ref;
116
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public B(object o) {
Ref = o;
}
~B() {
Console.WriteLine("Finalize instance of B");
}
}
class Test
{
static void Main() {
B b = new B(new A());
b = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
このプログラムは,クラスAのインスタンス及びクラスBのインスタンスを生成する。これら
のオブジェクトは,変数bに,値nullが代入された時点で,ゴミ集めの対象となる。この時点
の後,使用者の記述コードはこれらにアクセスできなくなるためである。出力は次のいずれかに
なる。
Finalize instance of A
Finalize instance of B
又は
Finalize instance of B
Finalize instance of A
これは,オブジェクトのゴミ集めの実行順序について,C#言語はどのような制約も課さないか
らである。
微妙な事例においては,“終了化に適格”と“ごみ集めに適格”との間の区別が重要となる。
using System;
class A
{
~A() {
Console.WriteLine("Finalize instance of A");
}
public void F() {
Console.WriteLine("A.F");
Test.RefA = this;
}
}
class B
117
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public A Ref;
~B() {
Console.WriteLine("Finalize instance of B");
Ref.F();
}
}
class Test
{
public static A RefA;
public static B RefB;
static void Main() {
RefB = new B();
RefA = new A();
RefB.Ref = RefA;
RefB = null;
RefA = null;
// A and B now eligible for finalization
GC.Collect();
GC.WaitForPendingFinalizers();
// B now eligible for collection, but A is not
if (RefA != null)
Console.WriteLine("RefA is not null");
}
}
このプログラムで,ゴミ集め子が,Bの終了化子の前にAの終了化子の実行を選択したならば,
このプログラムの出力は,次のようになり得る。
Finalize instance of A
Finalize instance of B
A.F
RefA is not null
Aのインスタンスが使われず,Aの終了化子が実行されたにもかかわらず,Aのメソッド(こ
の場合,F)が依然として別の終了化子から呼び出される可能性があることに注意する。また,
終了化子の実行によって,オブジェクトが再び主プログラムから使用できるようになる可能性が
ある。上の場合,Bの終了化子の実行が,それ以前は使われていなかったAのインスタンスを,
有効な参照RefAからアクセス可能にする原因となる。WaitForPendingFinalizersへの呼
出しの後,Bのインスタンスはごみ集めに適格となるが,Aのインスタンスは,参照RefAのた
めに適格にならない。
混乱や予期しない振る舞いを避けるために,終了化子では,そのオブジェクト自身のフィール
ドに格納されたデータに対してだけ後始末を実行し,参照されているオブジェクトや静的フィー
118
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ルドに対しては動作を一切行わないようにすることを推奨する。
10.10 実行順序
実行は,重大な実行点における実行スレッドの副作用が保持されるように,進行する。副作用とは,揮
発性のフィールドの読込み又は書出し,非揮発性の変数への書出し,外部資源への書出し,及び,例外の
送出とする。そのような副作用の順序が保持されなければならない重大な実行点とは,揮発性のフィール
ド(17.4.3参照),lock文(15.12参照)及びスレッドの生成並びに終了とする。実装は,次の制約に従う
限り,C#プログラムの実行の順序を自由に変えることができる。
− 実行スレッド内でデータの依存関係を保持する。すなわち,各変数の値は,元のプログラムの順序で
スレッドのすべての文を実行したかのように計算される必要がある。
− 初期化順序規則が保持される(17.4.4及び17.4.5参照)。
− 副作用の順序は,揮発性の読込み及び書出し(17.4.3参照)に関して保持される。加えて,その式の
値が使われていなくて,後で必要となるような副作用(メソッドの呼出し又は揮発性フィールドのア
クセスによる発生を含む。)が生成されていないと導出される場合には,実装は,式の一部を評価しな
くてもよい。(別のスレッドによって送出された例外などの)非同期イベントによってプログラム実行
が中断された場合は,観測可能な副作用が元のプログラムの実行順序で見ることができるとは保証さ
れない。
11 型
C#言語の型は,値型,参照型及び型仮引数型の三つに大別される。
≪型≫:
≪値型≫
≪参照型≫
≪型仮引数≫
型仮引数は総称の一部であり,25.1.1で詳細に示す。第4の型であるポインタは,安全でないコード内で
だけ用いてもよい。これについては,25.2で詳細に示す。
値型の変数は,変数自身に直接データを格納するが,参照型の変数はデータへの参照を格納するという
点で,両者は異なる。参照型の変数を,オブジェクトと呼ぶ。参照型では二つの変数から同じオブジェク
トを参照できるため,ある変数を操作することによって,他の変数が参照しているオブジェクトにも影響
を与える場合がある。値型の場合,各変数はデータの独自の複製を保持しているため,ある変数を操作し
ても他の変数に影響を与えない。
注記 変数がref仮引数又はout仮引数の場合,変数は独自の記憶域をもたず,別の変数の記憶域
を参照する。この場合,ref変数又はout変数は事実上,別の変数の別名であり,明確な変数
ではない。
C#の型体系は,すべての型の値をオブジェクトとして扱うことができるように統一されている。C#のす
べての型は,直接的又は間接的にobjectクラス型から派生するものとし,objectはすべての型の最終
的な基底クラスとする。参照型の値は,object型として値を見ることで,単純にオブジェクトとして扱
われる。値型の値は,ボックス化処理及びボックス化解除処理(11.3参照)によって,オブジェクトとし
て扱われる。
11.1 値型
値型は,構造体型又は列挙型のいずれかとする。C#は,単純型と呼ばれるあらかじめ定義された構造体
119
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
型の集合を提供する。単純型は,予約語によって識別する。
≪値型≫:
≪構造体型≫
≪列挙型≫
≪構造体型≫:
≪型名≫
≪単純型≫
≪null許容型≫
≪単純型≫:
≪数値型≫
bool
≪数値型≫:
≪整数型≫
≪浮動小数点型≫
decimal
≪整数型≫:
sbyte
byte
short
ushort
int
uint
long
ulong
char
≪浮動小数点型≫:
float
double
≪列挙型≫:
≪型名≫
≪null許容型≫:
≪null許容でない値型≫ ?
≪null許容でない値型≫:
≪列挙型≫
≪型名≫
≪単純型≫
すべての値型は,暗黙にobjectクラスを継承する。値型からはどのような型も派生することはできな
い,更に,値型は暗黙で封印されている(17.1.1.2参照)とする。
値型の変数は,常にその型の値を含む。参照型とは異なり,値型の値はnullになることはない。また,
その派生型のオブジェクトを参照することもできない。
120
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
値型の変数への代入は,代入される値の複製を生成する。これは,参照型の変数への代入と異なる点で
あり,参照型変数の場合,参照を複製するが,参照によって示されるオブジェクトを複製しない。
注記 すべての値型は,クラスSystem.ValueTypeを継承する。これそのものは,クラスobject
を継承する。値型からはどのような型も派生することはできず,値型は暗黙に封印されている
(17.1.1.2参照)。
System.ValueTypeそのものは,≪値型≫でないことに注意する。これは,そこからすべ
ての≪値型≫が自動的に派生する≪クラス型≫である。
11.1.1 System.ValueType型
すべての値型は,暗黙にクラスSystem.ValueTypeを継承する。これそのものは,クラスobjectを
継承する。
System.ValueTypeそのものは,値型でないことに注意する。これは,そこからすべての値型が自動
的に派生するクラス型である。
11.1.2 省略時構築子
すべての値型は,省略時構築子と呼ばれる仮引数なしの公開インスタンス構築子を暗黙に宣言する。省
略時構築子は,その値型の省略時の値と呼ばれるゼロで初期化されたインスタンスを返す。
− いずれの≪単純型≫の場合も,省略時の値は,すべてゼロのビットパターンで生成される値とする。
・ sbyte,byte,short,ushort,int,uint,long及びulongに対する省略時の値は0とする。
・ charに対する省略時の値は,'¥x0000'とする。
・ floatに対する省略時の値は,0.0fとする。
・ doubleに対する省略時の値は,0.0dとする。
・ decimalに対する省略時の値は,0mとする。
・ boolに対する省略時の値は,falseとする。
− ≪列挙型≫に対する省略時の値は,0とする。
− ≪構造体型≫に対する省略時の値は,すべての値型フィールドにそれぞれの型の省略時の値が設定さ
れて,すべての参照型フィールドにnullが設定された値とする。
− ≪null許容型≫に対する省略時の値は,HasValueがfalseを返す値とする。
他のインスタンス構築子と同様に,値型の省略時構築子もnew演算子を使って呼び出される。
注記 この要件は,実装で構築子呼出しを実際に生成することを意図するものではない。これは,効
率化のためである。明らかにしておく。値型の場合,省略時値式(14.5.14参照)は,省略時構
築子を使用した場合と同じ結果を生成する。
例 次の例では,変数i,j及びkはすべてゼロに初期化される。
class A
{
void F() {
int i = 0;
int j = new int();
int k = default(int);
}
}
すべての値型に仮引数なしの公開インスタンス構築子を暗黙で宣言しているため,構造体型に対して仮
121
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
引数なしの構築子の明示的な宣言を含めてはならない。ただし,仮引数付きのインスタンス構築子を構造
体型で宣言してもよい(18.3.8参照)。
11.1.3 構造体型
構造体型は,定数,フィールド,メソッド,特性,添字子,演算子,インスタンス構築子,静的構築子
及び入れ子になった型を宣言できる値型とする。構造型については,箇条18で規定する。
11.1.4 単純型
C#は,単純型と呼ばれるあらかじめ定義された構造体型を提供する。単純型は,予約語を使って示され
るが,これらの予約語は,単にSystem名前空間であらかじめ定義された構造体型に対する別名とする。
予約語とその構造体型との関係を次の表に示す。
予約語
元になっている型
sbyte
System.SByte
byte
System.Byte
short
System.Int16
ushort
System.UInt16
int
System.Int32
uint
System.UInt32
long
System.Int64
ulong
System.UInt64
char
System.Char
float
System.Single
double
System.Double
bool
System.Boolean
decimal
System.Decimal
単純型は,構造体型に対する別名であることから,すべての単純型にはメンバがある。
例 例えば,intはSystem.Int32で宣言されたメンバ及びSystem.Objectから継承するメンバ
をもち,次のような文を使用できる。
int i = int.MaxValue;
// System.Int32.MaxValue constant
string s = i.ToString();
// System.Int32.ToString() instance
method
string t = 123.ToString();
// System.Int32.ToString() instance
method
単純型に対しては,他の構造体型と異なり,次の追加操作を行ってもよい。
− 大部分の単純型では,≪リテラル≫を記述することで値を生成してもよい(9.4.4参照)。
例 例えば,123は型intのリテラルであり,“a”は型charのリテラルである。
C#では,一般に構造体型のリテラルを提供しない。
− 式の演算対象がすべて単純型定数のとき,コンパイラは式をコンパイル時に評価することができる。
このような式のことを≪定数式≫(14.16参照)と呼ぶ。他の構造体型で定義された演算子を含む式は,
定数式とはみなさない。
− const宣言を使うことで,単純型の定数(17.3参照)を宣言してもよい。単純型以外の構造体型を定
数とすることはできないが,static readlonyフィールドを用いて同様の効果が得られる。
122
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 単純型にかかわる変換が,他の構造体型で定義された変換演算子の評価で使用されることはあるが,
利用者定義の変換演算子を別の利用者定義演算子の評価(13.4.2参照)の中で使用することはできな
い。
11.1.5 整数型
C#は,整数型としてsbyte,byte,short,ushort,int,uint,long,ulong及びcharの9種
類を提供する。整数型のそれぞれの値域及び大きさを,次に示す。
− sbyte型は,−128から127までの値をもつ,8ビットの符号付き整数を表す。
− byte型は,0から255までの値をもつ,8ビットの符号なし整数を表す。
− short型は,−32768から32767までの値をもつ,16ビットの符号付き整数を表す。
− ushort型は,0から65535までの値をもつ,16ビットの符号なし整数を表す。
− int型は,−2147483648から2147483647までの値をもつ,32ビットの符号付き整数を表す。
− uint型は,0から4294967295までの値をもつ,32ビットの符号なし整数を表す。
− long型は,−9223372036854775808から9223372036854775807までの値をもつ,64ビットの符号付
き整数を表す。
− ulong型は,0から18446744073709551615までの値をもつ,64ビットの符号なし整数を表す。
− char型は,符号なし16ビット整数値を表し,値の範囲は0〜65535とする。char型に設定してもよ
い値の集合は,Unicode文字集合に対応している。
注記 char型は,ushort型と同じ値を表現するが,一方の型に対して実行できる演算のすべて
が,他方の型に対して実行できるわけではない。それぞれ,一方の型に対してだけ実行で
きる演算がある。
整数型の単項演算子又は2項演算子は,常に,符号付き32ビット精度,符号なし32ビット精度,符号
付き64ビット精度,又は符号なし64ビット精度の演算を行う。これについては,箇条14で詳細に示す。
char型は整数型に分類されるが,他の整数型とは次の二つの点で異なる。
− 他の型からchar型への暗黙の型変換がない。特に,sbyte,byte及びushortの各型で表現でき
る値域は,char型を使って完全に表現することが可能だが,sbyte,byte,ushortからcharへ
の暗黙の型変換は存在しない。
− char型の定数は,文字リテラルとして記述するか,char型へのキャストと組み合わせて整数リテラ
ルとして記述しなければならない。
例 例えば,(char) 10は,'¥x000A'と同じである。
checked及びuncheckedの各演算子及び文(14.5.12参照)は,整数型の算術演算及び変換に対して
けた(桁)あふれ検査を制御するのに使用する。C#は,checked文脈では,けた(桁)あふれが発生す
ると,コンパイル時エラーになるか,又はSystem.OverflowExceptionを送出する。unchecked文
脈では,けた(桁)あふれは無視され,結果が格納される型に収まらない上位ビットは破棄される。
11.1.6 浮動小数点型
C#で使われる浮動小数点型は,float及びdoubleの2種類とする。float型及びdouble型は,そ
れぞれ32ビット単精度及び64ビット倍精度のIEC 60559書式を使って表現され,次の値を表現すること
が可能とする。
− 正のゼロ及び負のゼロ。ほとんどの場合,正のゼロ及び負のゼロは,単純な値ゼロと全く同じ動作を
示す。ただし,ある種の演算(14.7参照)では,正のゼロと負のゼロを区別する。
− 正及び負の無限大。無限大は,ゼロ以外の値をゼロで除した場合などに発生する。
123
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例 例えば,1.0 / 0.0は正の無限大になり,‒1.0 / 0.0は負の無限大になる。
− 非数値。しばしばNaNと省略表記される。NaNは,ゼロをゼロで除すなどの不当な浮動小数点演算が
行われた場合に発生する。
− s × m × 2eの形式で表される有限のゼロ以外の値。sは1又は-1で,m及びeは浮動小数点型の種
類によって決まる。floatの場合は,0 < m < 224及び-149 ≦ e ≦ 104で,doubleの場合は,
0 < m < 253及び-1075 ≦ e ≦ 970とする。正規化されない浮動小数点数は,有効なゼロ以外の
値とみなす。C#は,その適合実装の正規化されない浮動小数点数については触れず,禁止も奨励もし
ない。
float型は,7けた(桁)の有効けた数で,約1.5 × 10-45〜3.4 × 1038の範囲の値を表現すること
が可能とする。
double型は,15〜16けた(桁)の有効けた数で,約5.0 × 10-324 〜 1.7 × 10308の範囲の値を表
現することが可能とする。
代入演算子を含む浮動小数点演算子では,例外は発生しない。浮動小数点演算で例外状況が発生した場
合は,次の規則に従って,ゼロ,無限大又はNaNを生成する。
− 浮動小数点演算の結果は,結果格納先の書式において,表現可能な直近の値に丸める。この結果,ゼ
ロ以外の値がゼロに丸められる場合がある。
− 浮動小数点演算の結果,値の絶対値が結果格納先の書式に対して大き過ぎる場合は,演算の結果は正
の無限大又は負の無限大とする。
− 浮動小数点演算が無効である場合は,演算の結果はNaNとする。
− 浮動小数点演算の演算対象の一方又は両方がNaNである場合は,演算の結果はNaNとする。
浮動小数点演算は,結果の型より高い精度で実行してもよい。
例 例えば,double型よりも値域が広くより正確な“extended”又は“long double”浮動小数
点型を支援するハードウェアアーキテクチャでは,浮動小数点演算を暗黙によって精度の高い型
を使って実行する場合がある。このようなハードウェアアーキテクチャが,低精度の浮動小数点
数を扱うときにも高負荷の演算を行う設計になっている場合,性能及び精度の両方を低下させる
ような実装を強制することは望ましくないため,C#ではどのような浮動小数点演算にも高精度デ
ータ型を使用することを容認している。より正確な結果が必要な場合を除き,これによって大き
な違いが出ることはあまりない。ただし,x * y / zの形式の式で,乗算の結果がdoubleの
値域を超え,除算によって再び結果がdoubleの範囲に戻る場合には,より大きい範囲で式が評
価されるため,無限大ではなく有限値が生成される場合がある。
11.1.7 decimal(10進実数)型
decimal型は,財務計算や通貨計算に適した128ビットデータ型とする。decimal型は,少なくとも
28けた(桁)以上の有効けた数で,1×10−28から1×1028までの範囲の値を表現することが可能とする。
decimal型の有限の値の集合は,(−1)s×c×10−eの形式とする。sは0又は1,係数cは0≦c<Cmax,
小数部けた数eはEmin≦e≦Emax,Cmaxは少なくとも1×1028以上,Emin≦0,Emax≧28とする。decimal
型は,符号付きゼロ,無限大又は非数値(NaN)に対応していなくてもよい。
decimal値は,仮数部が整数で指数部が10の累乗の数として表現される。絶対値が1.0m未満の
decimal型の値は,少なくとも小数点以下第28けたまでは正確とする。絶対値が1.0m以上の場合の
decimal型の値は,少なくとも28けたまでは正確とする。float型及びdouble型とは異なり,decimal
型では,0.1のような10進小数を正確に表すことが可能とする。float型やdouble型では,このような
124
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
数値は無限の2進展開になることが多く,丸め誤差が発生する可能性が高くなる。
decimal型の値どうしの演算結果は,正確な結果(各演算子に対して定義された小数部けた数を保持し
た値)を計算してから,表現に合わせて丸められた値とする。結果は,最も近い表現できる値に丸める。
表現できる二つの値との差が等しい場合は,JIS Z 8401の2 c) 規則Aに従って最下位のけた(桁)位置が
偶数の値に丸められる。この方式は,“偶数丸め方式”と呼ばれる。結果は,少なくとも28けたまで正確
とする。値を丸めると0以外の値が0になる場合があることに注意する。
decimal型の算術演算の結果,絶対値が大き過ぎてdecimal形式で表すことができない場合,
System.OverflowExceptionを送出する。
decimal型は高い精度を備えているが,値域は浮動小数点型ほど大きくないことがある。したがって,
浮動小数点型からdecimal型への変換では,けた(桁)あふれ例外が発生する可能性があり,decimal
型から浮動小数点型への変換では,精度の損失又はけた(桁)あふれ例外が発生する可能性がある。この
ような理由から,浮動小数点型とdecimal型との間では暗黙の型変換は存在せず,明示的なキャストな
しに,浮動小数点型及びdecimal型の演算対象を一つの式の中に直接混在させると,コンパイル時エラ
ーになる。
11.1.8 bool型
bool型は,真理値の論理的な量を表現する。bool型は,true及びfalseの二つの値をとることがで
きる。
bool型と他の型との間には,標準的な型変換は存在しない。特に,bool型と整数型とは明確に区別さ
れており,整数値の代わりに真理値を使ったり,真理値の代わりに整数値を使ったりしてはならない。
注記 C言語及びC++言語では,整数値若しくは浮動小数点値のゼロ,又はnullポインタは,真理
値のfalseに変換できる。整数値若しくは浮動小数点値の非ゼロ,又は非nullポインタは,
真理値のtrueに変換できる。C#では,このような型変換は,整数値又は浮動小数点値とゼロ
とを明示的に比較するか,オブジェクト参照とnullとを明示的に比較することで行う。
11.1.9 列挙型
列挙型は,名前付き定数をもつ特別な型とする。すべての列挙型は,基礎とする型をもつ。byte,sbyte,
short,ushort,int,uint,long又はulongのいずれかを基にする必要がある。列挙型は,列挙宣
言を通じて定義する(21.1参照)。すべての列挙型の直接の基底型は,クラスSystem.Enumである。
System.Enumの直接の基底クラスは,System.ValueTypeである。
11.2 参照型
参照型は,クラス型,インタフェース型,配列型又は委譲型のいずれかとする。
≪参照型≫:
≪クラス型≫
≪インタフェース型≫
≪配列型≫
≪委譲型≫
≪クラス型≫:
≪型名≫
object
string
≪インタフェース型≫:
125
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪型名≫
≪配列型≫:
≪非配列型≫ ≪位階指定子群≫
≪非配列型≫:
≪値型≫
≪クラス型≫
≪インタフェース型≫
≪委譲型≫
≪型仮引数≫
≪位階指定子群≫:
≪位階指定子≫
≪位階指定子群≫ ≪位階指定子≫
≪位階指定子≫:
[ ≪次元区切り子群≫opt ]
≪次元区切り子群≫:
,
≪次元区切り子群≫ ,
≪委譲型≫:
≪型名≫
参照型の値は,オブジェクトと呼ばれる,型のインスタンスへの参照とする。特別な値nullは,すべ
ての参照型と互換性があり,インスタンスが存在しないことを示す。
11.2.1 クラス型
クラス型は,データメンバ(定数及びフィールド),関数メンバ(メソッド,特性,イベント,添字子,
演算子,インスタンス構築子,終了化子及び静的構築子),並びに入れ子になった型を含むデータ構造を定
義する。クラス型は,継承してもよい。継承とは,派生クラスが基底クラスを拡張及び限定するための機
構とする。クラス型のインスタンスは,≪オブジェクト生成式≫を使って生成する(14.5.10.1参照)。
クラス型については,箇条17で規定する。
11.2.2 object型
クラス型objectは,他のすべての型に対する最終的な基底クラスとなる。C#のすべての型は,クラス
型objectから直接又は間接に派生する。
キーワードobjectは,あらかじめ定義されたクラスSystem.Objectに対する単なる別名である。
11.2.3 string(文字列)型
string(文字列)型は,objectから直接継承した封印クラス型とする。クラスstringのインスタ
ンスは,Unicodeの文字列を表す。
string(文字列)型の値は,文字列リテラルとして記述できる(9.4.4参照)。
キーワードstringは,あらかじめ定義されたクラスSystem.Stringに対する単なる別名である。
11.2.4 インタフェース型
インタフェースは,契約を定義する。インタフェースを実装するクラス及び構造体は,その契約と一致
しなければならない。インタフェースは,複数の基底インタフェースから継承してもよい。また,クラス
及び構造体は,複数のインタフェースを実装してもよい。
126
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
インタフェース型については,箇条20で規定する。
11.2.5 配列型
配列は,算出された添字を用いてアクセスされる0個以上の変数を含むデータ構造とする。配列に含ま
れる変数は,配列要素とも呼ばれ,すべて同一の型でなければならない。この型を配列の要素型と呼ぶ。
配列型については,箇条19で規定する。
11.2.6 委譲型
委譲は,一つ以上のメソッドを参照するデータ構造とする。また,インスタンスメソッドの場合は,対
応するオブジェクトインスタンスも参照する。
注記 委譲と最も近いC又はC++の機能は関数ポインタであるが,関数ポインタでは静的関数だけを
参照できるのに対し,委譲では静的メソッドとインスタンスメソッドの両方を参照できる。後
者の場合,委譲は,メソッドの入口点への参照だけでなく,メソッドを呼び出すオブジェクト
インスタンスへの参照も格納する。
委譲型については,箇条22で規定する。
11.2.7 空型
nullリテラル(9.4.4.6参照)は,null値に評価される。null値は,参照先のオブジェクト又は配列を
一切もたない参照,又は値の欠如を示すために使用する。空型は値としてnull値だけをもつ。したがっ
て,型が空型の式は,常にnull値に評価される。空型を明示的に書き出す方法はないため,宣言された
型で空型を使用することはできない。
さらに,空型は,型仮引数(25.6.4参照)で推論される型になることもない。
11.3 ボックス化及びボックス化解除
ボックス化及びボックス化解除は,C#の型体系の中心的な概念である。これは,≪値型≫と≪参照型≫
との間の橋渡しをし,≪値型≫の値と型objectとの間で双方向の型変換を可能にする。ボックス化及び
ボックス化解除の機能によって,型体系を統一的に見ることができ,すべての型の値を最終的にオブジェ
クトとして扱うことが可能になる。
11.3.1 ボックス化変換
ボックス化変換は,任意の≪null許容でない値型≫からobject型,System.ValueType,又は,≪
null許容でない値型≫が実装する任意の≪インタフェース型≫への暗黙の変換を行う。さらに,ボック
ス化変換は,任意の列挙型からSystem.Enumへの暗黙の変換も行う。≪null許容でない値型≫の値の
ボックス化では,オブジェクトインスタンスの割当て,及びそのインスタンスへの≪値型≫の値の複製を
行う。また,ボックス化変換は≪null許容型≫でも許可される。null許容型T?がボックス化変換によ
って変換される型の集合は,Tと同じ型の集合になる。null許容型T?からのボックス化変換は,次のよ
うに処理される。
− 変換元の値がnull(HasValue特性がfalse)である場合,結果は変換先の型のnull参照になる。
− それ以外の場合,結果は,変換元の値の包装解除とボックス化によって生成された,ボックス化され
たTへの参照になる。
注記 この結果,vがnull許容型の値,vbがvのボックス化の結果である場合,vb==null,
v==null,null==vb及びnull==vという四つの式は同じ結果になる。
≪値型≫の値をボックス化する実際の処理は,その型に対するボックス化クラスの存在を仮
定すると理解が容易となる。
例 任意の≪値型≫Tに対して,ボックス化クラスは,あたかも次のように宣言されたかのよう
127
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
に振る舞う。
sealed class T̲Box
{
T value;
public T̲Box(T t) {
value = t;
}
}
このようにすると,T型の値vのボックス化では,式new T̲Box(v)が実行された後,結
果として生成されるインスタンスが型objectの値として返される。次に例を示す。
int i = 123;
object box = i;
上の文は,概念的に次の文に対応する。
int i = 123;
object box = new int̲Box(i);
上のT̲Box及びint̲Boxのようなボックス化クラスは実際には存在せず,ボックス化され
た値の動的な型は実際にはクラス型ではない。実際は,型Tのボックス化された値は動的な型
Tをもち,is演算子を使って動的な型の検査を行うことによって,型Tを参照できる。
例 次に例を示す。
int i = 123;
object box = i;
if (box is int) {
Console.Write("Box contains an int");
}
この例では,画面に“Box contains an int”という文字列が出力される。
ボックス化変換は,ボックス化される値の複製を作成することを意味する。これは,≪参照
型≫から型objectへの変換とは異なる。その場合の値は,引き続き同じインスタンスを指し
ており,単に型objectの派生型とみなされるにすぎない。
例 例えば,次の宣言を考える。
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
この宣言に対する次の文について考える。
Point p = new Point(10, 10);
object box = p;
128
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
p.x = 20;
Console.Write(((Point)box).x);
この例では,boxへのpの代入において発生する暗黙のボックス化操作によってpの値が
複製されるため,10という値が画面に出力される。Pointを構造体ではなく,classとし
て宣言すると,p及びboxは同じインスタンスを参照するため,20という値が出力される。
11.3.2 ボックス化解除変換
ボックス化解除変換は,型object又はSystem.ValueTypeから任意の≪null許容でない値型≫へ,
又は任意の≪インタフェース型≫からその≪インタフェース型≫を実装している任意の≪null許容でな
い値型≫への明示的な変換を行う。さらにボックス化解除変換は,System.Enumから任意の列挙型への
明示的な変換も行う。ボックス化解除の操作では,最初にオブジェクトインスタンスが特定の≪値型≫の
ボックス化された値であることが検査された後,値がインスタンスから複製される。
null許容型T?のボックス化解除変換では,Tと同じ型の集合からの変換を行う。ボックス化解除変換
は,object又はSystem.ValueTypeから任意の≪null許容型≫へ,又は任意の≪インタフェース型
≫から,基礎とする型がその≪インタフェース型≫を実装している任意の≪null許容型≫への明示的な
変換を行う。さらにボックス化解除変換は,System.Enumから,基礎とする型が列挙型である任意の≪
null許容型≫への明示的な変換も行う。
null許容型T?へのボックス化解除変換は,次のように処理される。
− 変換元がnull参照である場合,結果は型T?のnull値になる。
− 変換元がボックス化されたTへの参照である場合,結果は変換元のボックス化解除と包装によって生
成されたT?になる。
− それ以外の場合,System.InvalidCastExceptionを送出する。
11.3.1で説明した仮想のボックス化クラスを用いると,オブジェクトboxから≪値型≫Tへのボックス
化解除変換では,式((T̲Box)box).valueが実行される。
例 次の文を考える。
object box = 123;
int i = (int)box;
上の文は,概念的に次の文に対応する。
object box = new int̲Box(123);
int i = ((int̲Box)box).value;
与えられた≪値型≫に対するボックス化解除変換を実行時に成功させるには,変換前の演算対象の値が,
その≪値型≫の値のボックス化によって以前に生成されたオブジェクトを参照していなければならない。
変換前の演算対象の値がnullの場合は,System.NullReferenceExceptionを送出する。変換前の
演算対象の値が非互換なオブジェクトへの参照である場合は,System.InvalidCastExceptionを送出
する。
11.4 null許容型
≪null許容型≫は,基礎とする型の値とnull表示子を組み合わせた構造体とする。より具体的にい
うと,null許容型のインスタンスには,bool型のHasValueと,null許容型の基礎とする型のValue
という二つの公開読込み専用特性がある。HasValueは,非nullのインスタンスではtrue,nullのイ
ンスタンスではfalseになる。HasValueがtrueである場合,Value特性は,保持している値を返す。
HasValueがfalseである場合,Value特性にアクセスしようとすると例外が発生する。null許容型は,
129
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
値型(11.1参照)に分類される。
≪null許容型≫:
≪null許容でない値型≫ ?
null許容型において,?修飾子の前に指定した≪null許容でない値型≫は,null許容型の≪基礎とす
る型≫と呼ぶ。null許容型の基礎とする型は,任意のnull許容でない値型,又はnull許容でない値型
に制約(25.7参照)された任意の型仮引数(つまり,struct制約がある任意の型仮引数)でなければな
らない。null許容型の基礎とする型は,null許容型又は参照型のいずれでもない。
例 int??及びstring?は不正な型である。
null許容型は,null許容型が基礎とする型のすべての値と,追加のnull値で表すことができる。
T?及びSystem.Nullable<T>は,同じ型を意味する。
11.4.1 メンバ
null許容型T?のインスタンスは,次の二つの公開読込み専用特性をもつ。
− bool型のHasValue特性
− T型のValue特性
HasValueがtrueのインスタンスを非nullという。非nullのインスタンスは既知の値を含み,Value
はその値を返す。
HasValueがfalseのインスタンスをnullという。nullのインスタンスのValueを読み込もうとし
た場合,System.InvalidOperationExceptionを送出する。
省略時構築子に加えて,すべてのnull許容型T?は,型Tの実引数を一つとる公開構築子をもつ。型T
に値xを与えて
new T?(x)
の形式で構築子を呼び出すと,Value特性がxであるT?の非nullのインスタンスが作成される。
明示的にnull許容型の構築子を呼び出す必要は一切ない。同様の機能がTからT?への暗黙の変換で提
供されるためである。
11.4.2 実装インタフェース
T?の形式の型は,System.Nullable<T>の別名であり,インタフェース(箇条20参照)を実装しない。
特に,このことは,基礎とする型Tが実装するインタフェースを,一切実装しないことを意味する。
12 変数
変数は,格納場所を表す。すべての変数には型があり,型によって変数に格納できる値が決まる。C#は
型安全な言語であり,C#コンパイラは,変数には常に適切な型の値が格納されることを保証する。変数の
値は,代入でき,++演算子及び--演算子を使って変更できる。
変数から値を取得するには,先に変数を確実に代入された状態(12.3参照)にしておかなければならな
い。
12.1〜12.5で規定するとおり,変数の状態は,初期代入あり又は初期代入なしのいずれかとする。初期
代入ありの変数は,適切に定義された初期値をもち,常に確実に代入されているものとみなされる。初期
代入なしの変数は,初期値をもたない。初期代入なしの変数が特定の位置において確実に代入されている
ものとみなされるには,その位置に至る可能性のあるすべての実行経路において,その変数への代入が行
われていなければならない。
130
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
12.1 変数の種類
C#で定義されている変数の種類は,静的変数,インスタンス変数,配列要素,値仮引数,参照仮引数,
出力仮引数及び局所変数の7種類とする。12.1.1〜12.1.7では,これらについて規定する。
例 次に例を示す。
class A
{
public static int x;
int y;
void F(int[] v, int a, ref int b, out int c) {
int i = 1;
c = a + b++;
}
}
上の例においてXは静的変数,yはインスタンス変数,v[0]は配列要素,aは値仮引数,bは
参照仮引数,cは出力仮引数,そして,iは局所変数である。
12.1.1 静的変数
修飾子staticを指定して宣言されたフィールドを,静的変数と呼ぶ。静的変数は,それを含む型に対
して静的構築子(17.11参照)が,実行される前から存在し,関連付けられたアプリケーション領域が消失
すると消失する。
静的変数の初期値は,変数の型の省略時の値(12.2参照)とする。
確実な代入の検査に当たっては,静的変数は初期代入ありとみなされる。
12.1.2 インスタンス変数
修飾子staticを指定せずに宣言されたフィールドは,インスタンス変数と呼ぶ。
12.1.2.1 クラスのインスタンス変数
クラスのインスタンス変数は,そのクラスの新しいインスタンスが生成されると存在するようになり,
そのインスタンスに対する参照がなくなってインスタンスの終了化子(もしあれば)が実行されると存在
しなくなる。
クラスのインスタンス変数の初期値は,変数の型の省略時の値(12.2参照)とする。
確実な代入の検査に当たっては,インスタンス変数は初期代入ありとみなされる。
12.1.2.2 構造体のインスタンス変数
構造体のインスタンス変数の有効期間は,それが属している構造体変数と全く同じとする。すなわち,
構造体型の変数が存在するようになるとその構造体のインスタンス変数も存在するようになり,構造体型
の変数が存在しなくなるとその構造体のインスタンス変数も存在しなくなる。
構造体のインスタンス変数の初期代入状態は,それを含む構造体変数の初期代入状態と同じとする。す
なわち,構造体変数が初期代入ありとみなされると,そのインスタンス変数も初期代入ありとみなされ,
構造体変数が初期代入なしとみなされると,そのインスタンス変数も初期代入なしとみなされる。
12.1.3 配列要素
配列の要素は,配列インスタンスが生成されると存在するようになり,その配列インスタンスに対する
参照がなくなると存在しなくなる。
配列の各要素の初期値は,配列要素の型の省略時の値(12.2参照)とする。
131
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
確実な代入の検査に当たっては,配列要素は初期代入ありとみなされる。
12.1.4 値仮引数
ref修飾子又はout修飾子を付けずに宣言された仮引数を,値仮引数と呼ぶ。
値仮引数は,仮引数が属している関数メンバ(メソッド,インスタンス構築子,アクセス子又は演算子)
が呼び出されると存在するようになり,呼出しで指定された実引数の値で初期化される。値仮引数は,関
数メンバが呼出し側に制御を返すと存在しなくなる。ただし,値仮引数が,無名メソッド(14.5.15.3.1参
照)又は関数メンバ本体が反復子ブロック(箇条26参照)によって補足される場合は除く
確実な代入の検査に当たっては,値仮引数は初期代入ありとみなされる。
12.1.5 参照仮引数
ref修飾子を使って宣言された仮引数を,参照仮引数と呼ぶ。
参照仮引数は,新しい記憶域を生成しない。参照仮引数は,関数メンバの呼出しで実引数として指定さ
れた変数と同じ記憶域を表現する。したがって,参照仮引数の値は,元の変数と常に同じとする。
参照仮引数には,確実な代入に関して次の規則が適用される。
注記 12.1.6で規定する出力仮引数に対する規則とは異なる。
− 関数メンバ呼出しの参照仮引数として変数を渡す前に,変数を確実に代入された状態(12.3参照)に
しなければならない。
− 関数メンバの中では,参照仮引数は初期代入ありとみなされる。
構造体型のインスタンスメソッド又はインスタンスアクセス子では,キーワードthisは,正確に構造
体型の参照仮引数として動作する(14.5.7参照)。
12.1.6 出力仮引数
out修飾子を使って宣言された仮引数を,出力仮引数と呼ぶ。
出力仮引数は,新しい記憶域の生成を行わない。出力仮引数は,関数メンバの呼出しで実引数として指
定された変数と同じ記憶域を表現する。したがって,出力仮引数の値は,元の変数と常に同じとする。
出力仮引数には,確実な代入に関して次の規則が適用される。
注記 12.1.5で規定する参照仮引数に対する規則とは異なる。
− 関数メンバ呼出しの出力仮引数として変数を渡す前に,変数を確実に代入された状態にする必要はな
い。
− 関数メンバの呼出しが正常完了した後,出力仮引数として渡された変数は,その実行経路で代入され
たとみなされる。
− 関数メンバの中では,出力仮引数は初期代入なしとみなされる。
− 関数メンバのすべての出力仮引数は,その関数メンバが返る前に確実に代入(12.3参照)されなけれ
ばならない。
構造体型のインスタンス構築子内部では,キーワードthisは,構築子の宣言に構築子初期化子が含ま
れるかどうかに基づいて,構造体型の出力仮引数又は参照仮引数のいずれかと完全に同じ動作をする
(14.5.7参照)。
12.1.7 局所変数
局所変数は,≪局所変数宣言≫,≪foreach文≫及び≪try文≫の≪特定catch節≫によって宣言さ
れる。≪foreach文≫の場合,局所変数は繰返し変数になる(15.8.4参照)。≪特定catch節≫の場合,
局所変数は例外変数になる(15.10参照)。≪foreach文≫又は≪特定catch節≫で宣言された局所変数
は,初期代入ありとみなされる。
132
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪局所変数宣言≫は,≪ブロック≫,≪for文≫,≪switch文≫又は≪using文≫の中で指定できる。
局所変数の有効期間は,プログラムの実行中に局所変数の記憶域が確保されている間とする。この有効
期間は,関連付けられた有効範囲の開始から,少なくともその有効範囲が終わるまでとする。内側の≪ブ
ロック≫に入る,メソッドを呼び出す,又は,反復子ブロックから値を取得することによって,現在の有
効範囲の実行が中断されるが,終了はしない。無名メソッドが局所変数を受け取ると,少なくともすべて
の参照している委譲がガーベジコレクションの対象になるまで,変数の有効期間は延長される(14.5.15.3.1
参照)。親の有効範囲が再帰的又は繰返しに呼び出される場合,呼び出されるたびに局所変数の新しいイン
スタンスが生成され,≪局所変数初期化子≫があれば,その局所変数初期化子が評価される。
注記 局所変数は,その有効範囲に入るたびに具現化される。この動作は,無名メソッドを含む利用
者コードで確認できる。
≪局所変数宣言≫で導入された局所変数は自動的には初期化されないので,省略時の値もない。このよ
うな局所変数は初期代入なしとみなされる。≪局所変数宣言≫では≪局所変数初期化子≫を指定してもよ
い。局所変数初期化子を指定すると,変数は,≪局所変数初期化子≫の中で使われている式の内部を除き,
その変数の有効範囲全体で確実に代入されているものとみなされる。
局所変数の有効範囲では,≪局所変数宣言子≫より前の記述位置でその局所変数を参照すると,コンパ
イル時エラーになる。
注記 局所変数の実際の有効期間は,実装依存とする。例えば,コンパイラは,ブロック内の局所変
数がそのブロックのごく一部でしか使用されないことを静的に決定できる場合がある。コンパ
イラは,この分析に基づいて,変数の記憶域の有効期間が,変数を含むブロックの有効期間よ
りも短いコードを生成する場合がある。
局所参照変数が参照する記憶域は,局所参照変数の有効期間にかかわらず個別に再利用され
る(10.9参照)。
12.2 省略時の値
次の種類の変数は,自動的に省略時の値に初期化される。
− 静的変数
− クラスインスタンスのインスタンス変数
− 配列要素
変数の省略時の値は,変数の型に依存し,次のとおり定められる。
− ≪値型≫の変数の場合,省略時の値は,その≪値型≫の省略時構築子(11.1.2参照)によって計算さ
れた値と同じとする。
− ≪参照型≫の場合,省略時の値はnullとする。
注記 省略時の値への初期化では,普通,メモリマネージャ又はゴミ集め子でメモリを全ビットゼロ
に初期化した後,変数に割り当てる。そのため,全ビットゼロの値を使ってnull参照を表す
のが便利となる。
null許容型の省略時の値は,HasValue特性がfalseのインスタンスとする。null許容型の省略時
の値のValue特性を参照した場合,System.InvalidOperationException型の例外が発生する。省
略時の値は,null許容型のnull値とも呼ばれる。null型(11.2.7参照)から任意のnull許容型に暗
黙の型変換が行われ,この型変換でその型のnull値が生成される。
12.3 確実な代入
コンパイラが関数メンバの実行コードの特定の位置について静的なフロー分析を行い(12.3.3参照),変
133
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
数が自動的に初期化されていること,又は少なくとも1回の代入の対象になっていることが証明された場
合,その変数はその位置において確実に代入されていると呼ばれる。
注記 簡単にいえば,確実な代入の規則は,次のとおりとする。
− 初期代入ありの変数(12.3.1参照)は,常に確実に代入されているとみなされる。
− 初期代入なしの変数(12.3.2参照)は,ある位置に到達するまでに通過する可能性のあるすべての実
行経路に次に示す少なくとも一つが含まれている場合,その位置においては確実に代入されているも
のとみなされる。
・ 変数が左演算対象として指定されている単純な代入(14.14.1参照)。
・ 変数を出力仮引数として渡す呼出し式(14.5.5参照)又はオブジェクト生成式(14.5.10.1参照)。
・ 局所変数の場合は,変数初期化子を含む局所変数宣言(15.5参照)。
上記の規則の基となる正式な規定については,12.3.1,12.3.2及び12.3.3による。
≪構造体型≫変数のインスタンス変数に関する確実に代入された状態は,全体としてだけではなく個別
にも追跡される。≪構造体型≫変数及びそのインスタンス変数には,上記の規則に加えて次の規則も適用
される。
− ≪構造体型≫変数が確実に代入されているとみなされる場合は,そこに含まれるインスタンス変数も
確実に代入されているとみなされる。
− ≪構造体型≫変数のすべてのインスタンス変数が確実に代入されているとみなされる場合は,その構
造体型の変数も確実に代入されているとみなされる。
確実な代入は,次の文脈における要件とする。
− 変数の値を取得するすべての位置において,その変数は,確実に代入されていなければならない。
注記 これによって,未定義の値が発生しないことが保証される。
− 式の中で使われている変数については,次の場合を除き,変数の値を取得するとみなす。
・ 変数が,単純な代入の左演算対象になっている場合。
・ 変数が,出力仮引数として渡されている場合。
・ 変数が≪構造体型≫変数で,メンバアクセスの左演算対象として指定されている場合。
− 変数は,参照仮引数として渡されるすべての位置において,確実に代入されていなければならない。
注記 これによって,呼び出される関数メンバは,参照仮引数を初期代入ありとみなしてもよい
ことが保証される。
− 関数メンバのすべての出力仮引数は,関数メンバが(return文によって,又は実行が関数メンバ本
体の終端に達したことで)制御を返すすべての位置において,確実に代入されていなければならない。
注記 これによって,関数メンバが出力仮引数で未定義の値を返さないことが保証されるので,
コンパイラは,出力仮引数として変数を受け取る関数メンバ呼出しが変数への代入と同じ
であるとみなしてもよい。
− ≪構造体型≫インスタンス構築子のthis変数は,インスタンス構築子が制御を返すすべての位置で
確実に代入されていなければならない。
12.3.1 初期代入ありの変数
次の種類の変数は,初期代入ありに分類される。
− 静的変数
− クラスインスタンスのインスタンス変数
− 初期代入ありの構造体変数のインスタンス変数
134
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 配列要素
− 値仮引数
− 参照仮引数
− catch節,foreach文又はusing文で宣言された変数
12.3.2 初期代入なしの変数
次の種類の変数は,初期代入なしに分類される。
− 初期代入なしの構造体変数のインスタンス変数
− 構築子初期化子がない構造体のインスタンス構築子のthis変数を含む出力仮引数
− catch節,foreach文又はusing文で宣言された局所変数を除く局所変数
12.3.3 確実な代入を判断するための厳密な規則
使用する各変数が確実に代入されていると決定するには,12.3.3で規定する内容に対応する処理をコン
パイラで使用しなければならない。
コンパイラは,初期代入なしの変数を一つ以上もつ関数メンバの本体を一つずつ処理する。初期代入な
しの変数vについて,コンパイラは,関数メンバ内の次に示すそれぞれの箇所で,変数vの確実に代入さ
れた状態を決定する。
− 各文の先頭
− 各文の終了点(15.1参照)
− 別の文又は文の終了点に制御を移すすべての遷移
− 各式の先頭
− 各式の末尾
確実な代入についてのvの状態は,次のいずれかになる。
− 確実に代入されている。この状態は,この箇所に至る可能性のあるすべての制御フローで,変数vに
値が代入されていることを示す。
− 確実には代入されていない。bool型の式の末尾では,変数への代入が確実には行われていない場合,
変数の状態は,次のいずれかの下位状態になり得る。ただし,必ずしもこれらの状態になるとは限ら
ない。
・ 真式の後では確実に代入されている。この状態は,真理式の値がtrueの場合はvに確実に代入さ
れるが,真理式がfalseの場合は必ずしも代入されないことを示す。
・ 偽式の後では確実に代入されている。この状態は,真理式の値がfalseの場合はvに確実に代入
されるが,真理式がtrueの場合は必ずしも代入されないことを示す。
それぞれの位置での変数vの状態は,次の規則に従って決定される。
12.3.3.1 文についての一般的な規則
− 関数メンバ本体の先頭では,vは確実には代入されていない。
− 到達不能な文の先頭では,vは確実に代入されている。
− その他すべての文の先頭での確実な代入についてのvの状態は,その文の先頭に制御を移すすべての
制御フロー遷移での確実な代入についてのvの状態を検査して決定される。このようなすべての制御
フロー遷移でvが確実に代入されている場合であって,その場合に限り,文の先頭でvは確実に代入
されている。遷移可能な制御フローの集合の決定には,文の到達可能性の検査(15.1参照)の場合と
同じ方法を使用する。
− ブロック文の終了点,及びchecked,unchecked,if,while,do,for,foreach,lock,using,
135
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
switchの各文の終了点での確実な代入についてのvの状態は,その文の終了点に制御を移すすべて
の制御フロー遷移での確実な代入についてのvの状態を検査して判断される。このようなすべての制
御フロー遷移でvに確実に代入されている場合は,文の終了点でvは確実に代入されている。それ以
外の場合,文の終了点でvは確実には代入されていない。遷移可能な制御フローの集合の決定には,
文の到達可能性の検査(15.1参照)の場合と同じ方法を使用する。
12.3.3.2 ブロック文,checked文及びunchecked文
ブロック内の文並びの最初の文(文並びが空の場合は,ブロックの終了点)への制御遷移での確実な代
入についてのvの状態は,ブロック文,checked文又はunchecked文より前の確実な代入についてのv
の状態と同じとする。
12.3.3.3 式文
式exprで構成される式文stmtの場合は,次の規則に従う。
− 式exprの先頭では,確実な代入についてのvの状態はstmtの先頭と同じとする。
− 式exprの末尾でvに確実に代入されている場合,stmtの終了点では確実に代入されている。それ
以外の場合,stmtの終了点では確実には代入されていない。
12.3.3.4 宣言文
− stmtを初期化子のない宣言文とするならば,stmtの終了点では,確実な代入についてのvの状態は
stmtの先頭と同じとする。
− stmtを初期化子をもつ宣言文とするならば,確実な代入についてのvの状態を決定するときには,
stmtをあたかも文並びであるかのようにみなす。文並びを,初期化子をもつ宣言ごとに一つの代入
文が(宣言の順序に従って)あるとして処理する。
12.3.3.5 if文
次の形式のif文stmtを考える。
if (expr)then-stmt else else-stmt
このstmtは,次の規則に従う。
− exprの先頭では,確実な代入についてのvの状態はstmtの先頭と同じとする。
− exprの末尾でvが確実に代入されている場合は,then-stmt又はelse-stmt(else節がないと
きはstmtの終了点)への制御フロー遷移でvは確実に代入されている。
− exprの末尾のvが“真式の後では確実に代入される”状態の場合,then-stmtへの制御フロー遷移
では確実に代入されるが,else-stmtへの制御フロー遷移,及びelse節がないときはstmtの終了
点への制御フロー遷移では確実には代入されない。
− exprの末尾のvが“偽式の後では確実に代入される”状態の場合,else-stmtへの制御フロー遷移
では確実に代入されるが,then-stmtへの制御フロー遷移では確実には代入されない。また,stmt
の終了点で確実に代入されているとみなされるのは,then-stmtの終了点で確実に代入されている
場合だけとする。
− 上記以外の場合は,then-stmtへの制御フロー遷移,又はelse-stmt(又は,else節がないとき
のstmtの終了点)への制御フロー遷移のいずれにおいても,vは確実には代入されていないとみな
す。
12.3.3.6 switch文
制御式exprをもつswitch文stmtは,次の規則に従う。
− exprの先頭での確実な代入についてのvの状態は,stmtの先頭のvの状態と同じとする。
136
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 到達可能なswitchブロックの文並びへの制御フロー遷移での確実な代入についてのvの状態は,
exprの末尾での確実な代入についてのexprの状態と同じとする。
12.3.3.7 while文
次の形式のwhile文stmtを考える。
while (expr)while-body
このstmtは,次の規則に従う。
− exprの先頭では,確実な代入についてのexprの状態はstmtの先頭と同じとする。
− exprの末尾でexprに確実に代入されている場合は,while-body及びstmtの終了点への制御フ
ロー遷移でexprは確実に代入されている。
− exprの末尾のexprが“真式の後では確実に代入される”状態の場合,while-bodyへの制御フロ
ー遷移では確実に代入されるが,stmtの終了点では確実には代入されていない。
− exprの末尾のexprが“偽式の後では確実に代入される”状態の場合は,stmtの終了点への制御フ
ロー遷移でexprは確実に代入されているが,while-bodyへの制御フロー遷移では確実には代入さ
れていない。
12.3.3.8 do文
次の形式のdo文stmtを考える。
do do-body while (expr );
このstmtは,次の規則に従う。
− stmtの先頭からdo-bodyへの制御フロー遷移での確実な代入についてのexprの状態は,stmtの
先頭での状態と同じとする。
− exprの先頭では,確実な代入についてのexprの状態はdo-bodyの終了点と同じとする。
− exprの末尾でexprが確実に代入されている場合は,stmtの終了点への制御フロー遷移でexprは
確実に代入されている。
− exprの末尾のexprが“偽式の後では確実に代入される”状態の場合は,stmtの終了点への制御フ
ロー遷移では,exprが確実に代入されているが,do-bodyへの制御フロー遷移では,確実には代入
されていない。
12.3.3.9 for文
次の形式のfor文の確実な代入の検査を考える。
for ( ≪for初期化子≫ ; ≪for条件≫ ; ≪for反復子≫ ) ≪埋込み文≫
この文は,次のように記述されているとみなされる。
{
≪for初期化子≫ ;
while ( ≪for条件≫ ) {
≪埋込み文≫ ;
LLoop:
≪for反復子≫ ;
}
}
この例では,for文に制御を移すcontinue文が,ラベルLLoopに制御を移すgoto文に変形されて
いる。for文で≪for条件≫を指定しなかった場合は,確実な代入の評価においては,上の展開された形
137
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
式中で≪for条件≫がtrueに置き換えられたとみなす。
12.3.3.10
break文,continue文及びgoto文
break,continue,gotoの各文によって発生した制御フロー遷移での確実な代入についてのexprの
状態は,文の先頭での確実な代入についてのexprの状態と同じとする。
12.3.3.11
throw文
次の形式のthrow文stmtを考える。
throw expr ;
このstmtは,次の規則に従う。
− exprの先頭での確実な代入についてのexprの状態は,stmtの先頭での確実な代入についてのexpr
の状態と同じとする。
12.3.3.12
return文
次の形式のreturn文stmtをまず考える。
return expr ;
このstmtは,次の規則に従う。
− exprの先頭での確実な代入についてのexprの状態は,stmtの先頭での確実な代入についてのexpr
の状態と同じとする。
− exprが出力仮引数の場合は,exprの後か,又はreturn文の外側にあるtry-finally若しくは
try-catch-finallyのfinallyブロックの末尾で,exprが確実に代入されていなければならな
い。
続いて,次の形式のreturn文,stmtを考える。
return ;
このstmtは,次の規則に従う。
− exprが出力仮引数の場合は,stmtの前か,return文の外側にあるtry-finally又は
try-catch-finallyのfinallyブロックの末尾で,exprが確実に代入されていなければならな
い。
12.3.3.13
try-catch文
次の形式のtry-catch文stmtを考える。
try ≪tryブロック≫
catch(…) ≪catchブロック1≫
…
catch(…) ≪catchブロックn≫
このstmtは,次の規則に従う。
− ≪tryブロック≫の先頭での確実な代入についてのexprの状態は,stmtの先頭での確実な代入に
ついてのexprの状態と同じとする。
− ≪catchブロックi≫(iは任意)の先頭での確実な代入についてのexprの状態は,stmtの先頭で
の確実な代入についてのexprの状態と同じとする。
− ≪tryブロック≫及びすべての≪catchブロックi≫(1〜nのすべてのi)の終了点でexprが確実
に代入されている場合であって,かつ,その場合に限り,stmtの終了点でexprは確実に代入され
ている。
138
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
12.3.3.14
try-finally文
次の形式のtry-finally文stmtを考える。
try ≪tryブロック≫ finally ≪finallyブロック≫
このstmtは,次の規則に従う。
− ≪tryブロック≫の先頭での確実な代入についてのexprの状態は,stmtの先頭での確実な代入に
ついてのexprの状態と同じとする。
− ≪finallyブロック≫の先頭での確実な代入についてのexprの状態は,stmtの先頭での確実な代
入についてのexprの状態と同じとする。
− stmtの終了点でexprの代入が確実に行われているとみなされるのは,次のいずれかの場合だけと
する。
・ ≪tryブロック≫の終了点で,exprが確実に代入されている場合。
・ ≪finallyブロック≫の終了点で,exprが確実に代入されている場合。
≪tryブロック≫内から≪tryブロック≫の外への制御フロー遷移(goto文など)が行われる場合で
も,≪finallyブロック≫の終了点でexprへの代入が確実に行われているときは,その制御フロー遷移
でもexprへの代入が確実に行われているとみなされる。exprが確実に代入されるとみなされるのは,
この場合だけではない。別の理由から,この制御フロー遷移でexprが確実に代入されるとみなされる場
合もある。
12.3.3.15
try-catch-finally文
次の形式のtry-catch-finally文の確実な代入の分析を考える。
try ≪tryブロック≫
catch(…) ≪catchブロック1≫
…
catch(…) ≪catchブロックn≫
finally ≪finallyブロック≫
この文は,次に示すように,try-catch文の外側にtry-finally文があるとみなされる。
try {
try ≪tryブロック≫
catch(…) ≪catchブロック1≫
…
catch(…) ≪catchブロックn≫
}
finally ≪finallyブロック≫
例 次の例は,try文(15.10参照)の異なるブロックが確実な代入に与える影響を示す。
class A
{
static void F() {
int i, j;
try {
goto LABEL;
// neither i nor j definitely assigned
139
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
i = 1;
// i definitely assigned
}
catch {
// neither i nor j definitely assigned
i = 3;
// i definitely assigned
}
finally {
// neither i nor j definitely assigned
j = 5;
// j definitely assigned
}
// i and j definitely assigned
LABEL:;
// j definitely assigned
}
}
12.3.3.16
foreach文
次の形式のforeach文stmtを考える。
foreach (≪型≫ ≪識別子≫ in expr ) ≪埋込み文≫
このstmtは,次の規則に従う。
− exprの先頭での確実な代入についてのvの状態は,stmtの先頭のvの状態と同じとする。
− ≪埋込み文≫の終了点,すなわちstmtの終了点への制御フロー遷移での確実な代入についてのvの
状態
は,exprの末尾でのvの状態と同じとする。
12.3.3.17
using文
次の形式のusing文stmtを考える。
using ( ≪資源取得子≫ ) ≪埋込み文≫
このstmtは,次の規則に従う。
− ≪資源取得子≫の先頭での確実な代入についてのexprの状態は,stmtの先頭でのexprの状態と同
じとする。
− ≪埋込み文≫への制御フロー遷移での確実な代入についてのexprの状態は,≪資源取得子≫の末尾
でのexprの状態と同じとする。
12.3.3.18
lock文
次の形式のlock文stmtを考える。
lock (expr ) ≪埋込み文≫
このstmtは,次の規則に従う。
− exprの先頭での確実な代入についてのexprの状態は,stmtの先頭のexprの状態と同じとする。
140
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− ≪埋込み文≫への制御フロー遷移での確実な代入についてのexprの状態は,exprの末尾でのexpr
の状態と同じとする。
12.3.3.19
単純な式の一般的な規則
リテラル(14.5.1参照),単純名(14.5.2参照),メンバアクセス式(14.5.4参照),添字指定されていな
いbaseアクセス式(14.5.8参照),typeof式(14.5.11参照)には,次の規則が適用される。
− このような式の末尾での確実な代入についてのexprの状態は,式の先頭での確実な代入についての
exprの状態と同じとする。
12.3.3.20
埋め込まれた式をもつ式の一般的な規則
括弧で囲まれた式(14.5.3参照),要素アクセス式(14.5.6参照),添字指定されたbaseアクセス式(14.5.8
参照),増加式及び減少式(14.5.9及び14.6.5参照),キャスト式(14.6.6参照),単項式+,-,~,*,2項
式+,-,*,/,%,<<,>>,<,<=,>,>=,==,!=,is,as,&,|,^(14.7,14.8,14.9及び14.10
参照),複合代入式(14.14.2参照),checked式及びunchecked式(14.5.12参照),配列生成式及び委譲
生成式(14.5.10参照)には,次の規則が適用される。
各式には,決められた順序で無条件に評価される一つ以上の部分式がある。
例 例えば,2項演算子%は,演算子の左辺を評価し,次に右辺を評価する。添字指定演算では,まず
添字指定された式を評価してから,左から右へ順に添字式を一つずつ評価する。
この順序で評価される部分式expr1,expr2,... exprnをもつ式exprは,次の規則に従う。
− expr1の先頭での確実な代入についてのexprの状態は,exprの先頭での確実に代入された状態と
同じとする。
− expri(iは1より大きい)の先頭での確実な代入についてのexprの状態は,expri1の末尾での
確実に代入された状態と同じとする。
− exprの末尾での確実な代入についてのexprの状態は,exprnの末尾での確実に代入された状態と
同じとする。
12.3.3.21
呼出し式及びオブジェクト生成式
呼出し式exprは,次の形式をとるとする。
primary-expression (arg1, arg2, …, argn)
オブジェクト生成式,exprは,次の形式をとるとする。
new type (arg1, arg2, …, argn)
− 呼出し式の場合,primary-expressionの前の確実な代入についてのexprの状態は,exprの前
のexprの状態と同じとする。
− 呼出し式の場合,arg1の前の確実な代入についてのexprの状態は,primary-expressionの後の
exprの状態と同じとする。
− オブジェクト生成式の場合,arg1の前の確実な代入についてのexprの状態は,exprの前のexpr
の状態と同じとする。
− 実引数argiについては,argiの後の確実な代入についてのexprの状態は,通常の式の規則によっ
て判断され,ref修飾子やout修飾子は無視される。
− iが1より大きい実引数argiについては,argiより前の確実な代入についてのexprの状態は,argi1
の後のexprの状態と同じとする。
− 任意の実引数で,変数exprをout実引数(“out expr”の形式の実引数)として渡した場合,expr
の後のexprは,代入が確実に行われている。それ以外の場合,exprの後のexprの状態は,argn
141
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
の後のexprの状態と同じとする。
12.3.3.22
単純な代入式
w = expr-rhsという形式の式exprは,次の規則に従う。
− wより前の確実な代入についてのvの状態は,exprより前の確実な代入についてのvの状態と同じ
とする。
− expr-rhsより前の確実な代入についてのvの状態は,wより後の確実な代入についてのvの状態と
同じとする。
− 変数wとvが同じならば,exprの後のvは,確実に代入されている。それ以外の場合,exprの後
の確実な代入についてのvの状態は,expr-rhsの後の確実な代入についてのvの状態と同じとする。
例 次のコードについて考える。
class A
{
static void F(int[] arr) {
int x;
arr[x = 1] = x;
// ok
}
}
この例の変数xは,二番目の単純な代入式の左辺の代入としてarr[x = 1]が評価された後,
確実に代入されているとみなされる
12.3.3.23
&&(二択条件論理積)式
expr-first && expr-secondの形式の式exprは,次の規則に従う。
− expr-firstより前の確実な代入についてのexprの状態は,exprより前の確実な代入についての
exprの状態と同じとする。
− expr-firstの後で,exprに代入が確実に行われているか,“真式の後では確実に代入される”状態
であるかのいずれかの場合,expr-secondの前のexprは確実に代入されている。それ以外の場合
は,確実には代入されていない。
− exprの後の確実な代入についてのexprの状態は,次の条件に基づいて決定される。
・ expr-firstの後でexprが確実に代入されている場合,exprの後のexprは確実に代入されて
いる。
・ それ以外の場合で,expr-secondの後でexprが確実に代入されており,更にexpr-firstの後
でexprが“偽式の後では確実に代入される”状態のときは,exprの後のexprは確実に代入され
ている。
・ それ以外の場合で,expr-secondの後でexprが確実に代入されているか,“真式の後では確実に
代入される”状態のときは,exprの後のexprは“真式の後では確実に代入される”状態とする。
・ それ以外の場合で,expr-firstの後のexprが“偽式の後では確実に代入される”状態であり,
更にexpr-secondの後のexprが“偽式の後では確実に代入される”状態のときは,exprの後
のexprは“偽式の後では確実に代入される”状態とする。
・ それ以外の場合,exprの後のexprは確実には代入されていない。
例 次に例を示す。
class A
142
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
static void F(int x, int y) {
int i;
if (x >= 0 && (i = y) >= 0) {
// i definitely assigned
}
else {
// i not definitely assigned
}
// i not definitely assigned
}
}
この例の変数iは,if文中の前の埋込み文では確実に代入されているとみなされるが,後ろ
の埋込み文では確実には代入されていない。メソッドFのif文で,最初の埋込み文では,この
埋込み文の実行より前に式(i = y)が必ず実行されているため,変数iは確実に代入されている。
それに対し,2番目の埋込み文では,x >= 0の検査がfalseになって変数iへの代入が行われ
ない場合があるため,変数iは確実には代入されていない。
12.3.3.24
||(二択条件論理和)式
expr-first || expr-secondの形式の式exprは,次の規則に従う。
− expr-firstより前の確実な代入についてのexprの状態は,exprより前の確実な代入についての
exprの状態と同じとする。
− expr-firstの後でexprが確実に代入されているか,“偽式の後では確実に代入される”状態であ
るかのいずれかならば,expr-secondの前のexprは確実に代入されている。それ以外は,確実に
は代入されていない。
− exprの後の確実な代入についてのexprの状態は,次の条件に基づいて決定される。
・ expr-firstの後でexprが確実に代入されている場合,exprの後のexprは確実に代入されて
いる。
・ それ以外の場合で,expr-secondの後のexprが確実に代入されており,更にexpr-firstの後
のexprが“真式の後では確実に代入される”状態のときは,exprの後のexprは確実に代入され
ている。
・ それ以外の場合で,expr-secondの後のexprが確実に代入されているか,“偽式の後では確実に
代入される”状態のときは,exprの後のexprは“偽式の後では確実に代入される”状態とする。
・ それ以外の場合で,expr-firstの後のexprが“真式の後では確実に代入される”状態であり,
更にexpr-secondの後のexprが“真式の後では確実に代入される”状態のときは,exprの後
のexprは“真式の後では確実に代入される”状態とする。
・ それ以外の場合,exprの後のexprは確実には代入されていない。
例 次に例を示す。
class A
{
static void G(int x, int y) {
143
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int i;
if (x >= 0 || (i = y) >= 0) {
// i not definitely assigned
}
else {
// i definitely assigned
}
// i not definitely assigned
}
}
この例の変数iは,if文中の後ろの埋込み文では確実に代入されているとみなされるが,前
の埋込み文では確実に代入されているとはみなされない。メソッドGのif文で,2番目の埋込
み文では,この埋込み文の実行より前に式(i = y)が必ず実行されているため,変数iは確実に
代入されている。それに対し,最初の埋込み文では,x >= 0の検査がtrueになって変数iへ
の代入が行われない場合があるため,変数iは確実には代入されていない。
12.3.3.25
!式
! expr-operandの形式の式exprは,次の規則に従う。
− expr-operandの前の確実な代入についてのexprの状態は,exprの前の確実な代入についての
exprの状態と同じとする。
− exprの後の確実な代入についてのexprの状態は,次の条件に基づいて決定される。
・ expr-operandの後のexprが確実に代入されている場合,exprの後のexprは確実に代入され
ている。
・ expr-operandの後のexprが確実に代入されていない場合,exprの後のexprは確実に代入さ
れていない。
・ expr-operandの後のexprの状態が“偽式の後では確実に代入される”場合,exprの後のexpr
は“真式の後では確実に代入される”状態とする。
・ expr-operandの後のexprの状態が“真式の後では確実に代入される”場合,exprの後のexpr
は“偽式の後では確実に代入される”状態とする。
12.3.3.26
?:式
expr-cond ? expr-true : expr-falseの形式の式exprは,次の規則に従う。
− expr-condの前の確実な代入についてのexprの状態は,exprの前のexprの状態と同じとする。
− expr-condの後のexprが確実に代入されているか,“真式の後では確実に代入される”状態かのい
ずれかである場合に限り,expr-trueの前のexprは確実に代入されている。
− expr-condの後のexprが確実に代入されているか,“偽式の後では確実に代入される”状態かのい
ずれかである場合に限り,expr-falseの前のexprは確実に代入される。
− exprの後の確実な代入についてのexprの状態は,次の条件に基づいて決定される。
・ expr-condが,値trueの定数式(14.15参照)ならば,exprの後の確実な代入についてのexpr
の状態は,expr-trueの後の確実な代入についてのexprの状態と同じとする。
・ それ以外の場合で,expr-condが,値falseの定数式(14.15参照)ならば,exprの後の確実な
代入についてのexprの状態は,expr-falseの後の確実な代入についてのexprの状態と同じと
144
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
する。
・ それ以外の場合で,expr-trueの後のexprが確実に代入されており,更に,expr-falseの後
のexprが確実に代入されているならば,exprの後のexprは確実に代入されている。
・ それ以外の場合は,exprの後のexprは確実には代入されていない。
12.3.3.27
無名メソッド式
無名メソッド(14.5.15参照)の仮引数の確実な代入についての状態は,名前付きメソッドの仮引数の場
合と同じとする。つまり,参照仮引数及び値仮引数は確実に初期代入があり,出力仮引数は初期代入がな
い。さらに,出力仮引数は,無名メソッドから正常に返る前に,確実に代入されなければならない(12.1.6
参照)。
≪無名メソッド式≫の≪ブロック≫への制御遷移における確実な代入についての外変数vの状態は,≪
無名メソッド式≫の前の確実な代入についてのvの状態と同じとする。つまり,外変数の確実な代入は,
≪無名メソッド式≫の文脈から継承する。≪無名メソッド式≫の≪ブロック≫内では,確実な代入は,通
常のブロック内と同じように展開する(12.3.3参照)。
≪無名メソッド式≫の後の確実な代入についての変数vの状態は,その≪無名メソッド式≫の前の確実
な代入についての状態と同じとする。
例 次に例を示す。
delegate bool Filter(int i);
void F() {
int max;
// Error, max is not definitely assigned
Filter f = delegate(int n) { return n < max; };
max = 5;
DoWork(f);
}
この例はコンパイル時エラーになる。無名メソッドの宣言でmaxが確実に代入されていないた
めである。
例 次に例を示す。
delegate void D();
void F() {
int n;
D d = delegate { n = 1; };
d();
// Error, n is not definitely assigned
Console.WriteLine(n);
}
この例もコンパイル時エラーになる。無名メソッドでのnの代入は,無名メソッドの外部にあ
るnの,確実な代入についての状態に影響を与えないためである。
12.3.3.28
yield文
次の形式のyield return文stmtを考える。
yield return expr ;
145
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− exprの先頭では,確実な代入についての変数vの状態はstmtの先頭と同じとする。
− 式exprの末尾で変数vに確実に代入されている場合,stmtの終了点では確実に代入されている。
それ以外の場合,stmtの終了点では確実には代入されていない。
12.3.3.29
??式
式exprは,次の形式をとるとする。
expr-first ?? expr-second
− expr-firstの前の確実な代入についてのvの状態は,exprの前の確実な代入についてのvの状態
と同じとする。
− expr-secondより前の確実な代入についてのvの状態は,expr-firstより後の確実な代入につい
ての状態と同じとする。
− exprより後の確実な代入についてのvの状態は,expr-firstより後の確実な代入についての状態
と同じとする。
12.4 変数参照
≪変数参照≫は,変数として分類される≪式≫とする。≪変数参照≫は,現在の値を取り出したり,新
しい値を格納したりするためにアクセスできる記憶域を表している。
≪変数参照≫:
≪式≫
注記 C及びC++では,≪変数参照≫は左辺値として知られている。
12.5 変数参照の分割不能性
bool,char,byte,sbyte,short,ushort,uint,int,float及び参照型の読込み及び書出し
は,分割してはならない。また,上記の型を基礎とする型をもつ列挙型の読込み及び書出しも分割しては
ならない。long,ulong,double,decimalを含むその他のデータ型,及び利用者定義型の読込み及び
書出しは,分割不可能である必要はない。この目的で設計されたライブラリ関数を除いて,増加や減少の
場合などでの分割不可能な読込み,変更及び書出しは保証されない。
13 変換
変換を行うことで,ある型の式を別の型として扱うことができる。変換には暗黙の変換と明示的な変換
があり,これによって,明示的なキャストが必要かどうかが決まる。
例 例えば,int型からlong型への変換は暗黙の変換であるため,int型の式は暗黙にlong型と
して扱うことができる。long型からint型への逆の変換は明示的な変換であり,明示的なキャ
ストが必要となる。
int a = 123;
long b = a;
// implicit conversion from int to long
int c = (int) b;
// explicit conversion from long to int
幾つかの変換は言語仕様で定義されている。プログラムは,そのプログラム固有の変換を定義できる
(13.4参照)。
注記 型に関する変換であることを明確にするために,型変換という用語を使うこともある。
13.1 暗黙の変換
次の変換は暗黙の変換に分類される。
− 恒等変換
146
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 暗黙の数値変換
− 暗黙の列挙変換
− 暗黙の参照変換
− ボックス化変換
− 暗黙の型仮引数変換
− 暗黙の定数式変換
− 利用者定義の暗黙の変換
− 無名メソッド式から互換性のある委譲型への暗黙の変換
− メソッドグループから互換性のある委譲型への暗黙の変換
− null型(11.2.7参照)から任意のnull許容型への変換
− 暗黙のnull許容変換
− もち上げられた利用者定義の暗黙の変換
暗黙の変換は,関数メンバ呼出し(14.4.3参照),キャスト式(14.6.6参照),代入(14.14参照)を含む,
様々な状況で起こり得る。
定義済みの暗黙の変換は常に成功し,例外が送出されることはない。
注記 適切に設計された利用者定義の暗黙の変換はその特質も開示しておくことを推奨する。
13.1.1 恒等変換
恒等変換は,任意の型をその型と同じ型に変換する。この変換は,要求された型を既にもつ実体につい
て,その型に変換可能であるといえるようにするためだけに存在する。
13.1.2 暗黙の数値変換
暗黙の数値変換には次のものがある。
− sbyteからshort,int,long,float,double又はdecimalへの変換。
− byteからshort,ushort,int,uint,long,ulong,float,double又はdecimalへの変
換。
− shortからint,long,float,double又はdecimalへの変換。
− ushortからint,uint,long,ulong,float,double又はdecimalへの変換。
− intからlong,float,double又はdecimalへの変換。
− uintからlong,ulong,float,double又はdecimalへの変換。
− longからfloat,double又はdecimalへの変換。
− ulongからfloat,double又はdecimalへの変換。
− charからushort,int,uint,long,ulong,float,double又はdecimalへの変換。
− floatからdoubleへの変換。
int,uint,long又はulongからfloatへの変換及びlong又はulongからdoubleへの変換では
精度の損失が発生することがあるが,値の絶対値の損失は発生しない。これ以外の暗黙の数値変換では,
どのような情報も失われない。
char型への暗黙の変換は存在しない。したがって,char型以外の整数型の値がchar型に自動的に変
換されることはない。
13.1.3 暗黙の列挙変換
暗黙の列挙変換は,≪10進整数リテラル≫の0を任意の≪列挙型≫に変換することを許す。
147
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
13.1.4 暗黙の参照変換
暗黙の参照変換には次のものがある。
− 任意の≪参照型≫からobjectへの変換。
− 任意の≪クラス型≫ Sから任意の≪クラス型≫ Tへの変換。ただし,SがTから派生している場合。
− 任意の≪クラス型≫ Sから任意の≪インタフェース型≫ Tへの変換。ただし,SがTを実装している
場合。
− 任意の≪インタフェース型≫ Sから≪インタフェース型≫ Tへの変換。ただし,SがTから派生して
いる場合。
− 要素型がSEの任意の≪配列型≫ Sから要素型がTEの≪配列型≫ Tへの変換。ただし,次の条件がす
べて満たされる場合。
・ S及びTは要素型だけが異なる。すなわち,S及びTの次元数が同じである。
・ SEからTEへの暗黙の参照変換が存在する。
− 一次元の≪配列型≫S[]からSystem.Collections.Generic.IList<S>及びこのインタフェース
の基底インタフェースへの変換。
− SからTへの暗黙の参照変換が存在する場合,一次元の≪配列型≫S[]から
System.Collections.Generic.IList<T>及びこのインタフェースの基底インタフェースへの変
換。
− 任意の≪配列型≫からSystem.Arrayへの変換。
− 任意の≪委譲型≫からSystem.Delegateへの変換。
− 任意の≪配列型≫からSystem.Arrayによって実装された任意のインタフェースへの変換。
− 任意の≪委譲型≫からSystem.ICloneableへの変換。
− null型(11.2.7参照)から任意の≪参照型≫への変換。
参照型(25.7参照)であることが分かっている≪型仮引数≫Tに対しては,次の暗黙の参照変換が存在
する。
− Tからその実効基底クラスCへの変換,TからCの任意の基底クラスへの変換及びTからCによって
実装された任意のインタフェースへの変換。
− TからTの実効インタフェース集合の中の≪インタフェース型≫I及びTからIの任意の基底インタ
フェースへの変換。
− TがUに依存するとき,Tから型仮引数Uへの変換(25.7参照)。
注記 Tは参照型であることが分かっているので,Tの有効範囲内では,Uのコンパイル時に参照
型であることが分かっていないとしても,Uの実行時型は常に参照型である。
− null型からTへの変換。
暗黙の参照変換は,常に成功することが証明されている≪参照型≫間の変換であるため,実行時検査は
必要ない。
暗黙又は明示的な参照変換では,変換されるオブジェクトの参照識別性が変わることはない。
注記 すなわち,参照変換が参照の型を変更する際に,参照されているオブジェクトの型又は値を変
更することはない。
13.1.5 ボックス化変換
ボックス化変換は任意の≪null許容でない値型≫をobject型若しくはSystem.ValueType型に暗
黙に変換すること,又は,任意の≪null許容でない値型≫をその≪null許容でない値型≫によって実装
148
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
される任意の≪インタフェース型≫に暗黙に変換することを許す。また,同様に任意の列挙型は
System.Enumに暗黙に変換できる。≪null許容でない値型≫の値のボックス化では,オブジェクトイン
スタンスの割当て及びそのインスタンスへの値のコピーが行われる。System.Enum型はすべての列挙型
の直接の基底クラスであるため(21.4参照),列挙型はSystem.Enum型にボックス化できる。
System.ValueType型はすべての構造体の直接の基底クラスであり(18.3.2参照),すべての列挙型の基
底クラスであるため,構造体及び列挙型はSystem.ValueType型にボックス化できる,
≪null許容型≫は,その≪null許容型≫が基礎とする型がもつボックス化変換の変換先の型と同じ集
合へのボックス化変換をもつ。≪null許容型≫の値に適用されるボックス化変換は次のように処理する。
− null許容型のHasValue特性が偽と評価された場合,ボックス化変換の結果は,適切な型のnull
参照とする。
− そうでなければ,結果は,null許容型の値のValue特性を評価した結果をボックス化することによ
って取得される。
どの参照型になるかが不明な≪型仮引数≫Tに対しては(25.7参照),コンパイル時には,Tに対する次
の変換をボックス化変換とみなす。実行時にTが値型の場合には,ボックス化変換として変換を行う。実
行時にTが参照型の場合には,暗黙の参照変換又は恒等変換として変換を行う。
− TからTの実効基底クラスCへの変換,Tから任意の基底クラスCへの変換及びTからCによって実
装された任意のインタフェースへの変換。
注記 CはSystem.Object型,System.ValueType型又はSystem.Enum型のうちのいずれ
かとする。そうでなければ,Tは参照型ということになり,この13.1.5の規定ではなく,
13.1.4の規定を適用することになる。
− TからTの実効インタフェース集合の中の任意の≪インタフェース型≫Iへの変換及びTからIの任
意の基底インタフェースへの変換。
ボックス化変換の詳細については11.3.1で規定する。
13.1.6 暗黙の型仮引数変換
13.1.6では,暗黙の参照変換及び暗黙のボックス化変換に分類されない型仮引数に対する暗黙の変換に
ついて詳説する。
どんな参照型になるかが分かっていない≪型仮引数≫Tについて,Tが型仮引数Uに依存する場合,T
からUへの暗黙の変換が存在する。実行時にTが値型,かつ,Uが参照型であった場合,ボックス化変換
として変換する。実行時にT及びUがいずれも値型であった場合,T及びUは同じ型でなければならず,
変換は行われない。実行時にTが参照型であった場合,Uは参照型でなければならず,暗黙の参照変換又
は恒等変換として変換する。
13.1.7 暗黙の定数式変換
暗黙の定数式変換は次の変換を可能にする。
− int型の≪定数式≫(14.15参照)はsbyte,byte,short,ushort,uint又はulong型に変換
できる。ただし,≪定数式≫の値が変換先の型の範囲内である場合とする。
− long型の≪定数式≫はulong型に変換できる。ただし,≪定数式≫の値が負ではない場合とする。
13.1.8 利用者定義の暗黙の変換
利用者定義の暗黙の変換は,標準暗黙変換(省略可能),利用者定義の暗黙による変換演算子の実行,別
の標準暗黙変換(省略可能)という順序で構成される。利用者定義の変換の評価についての正確な規則は
13.4.3で規定する。
149
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
13.2 明示的な変換
次の変換は明示的な変換に分類される。
− すべての暗黙の変換
− 明示的な数値変換
− 明示的な列挙変換
− 明示的な参照変換
− 明示的なインタフェース変換
− ボックス化解除変換
− 明示的な型仮引数変換
− 利用者定義の明示的な変換
− 明示的なnull許容変換
− もち上げられた利用者定義の明示的な変換
明示的な変換はキャスト式(14.6.6参照)中で発生し得る。
明示的な変換の集合には,暗黙の変換がすべて含まれる。
注記 これは冗長なキャスト式を記述してもよいことを意味する。
暗黙の変換でない明示的な変換とは,常に成功するとは保証できない変換,情報が失われる可能性のあ
る変換,及び,明示的に表記するに値するほど異なっている型の領域をまたいで行われる変換のこととす
る。
13.2.1 明示的な数値変換
明示的な数値変換は,ある≪数値型≫から別の≪数値型≫への暗黙の数値変換(13.1.2参照)が常に存
在するとは限らない場合の,変換とする。具体的には,次のものがある。
− sbyteからbyte,ushort,uint,ulong又はcharへの変換。
− byteからsbyte又はcharへの変換。
− shortからsbyte,byte,ushort,uint,ulong又はcharへの変換。
− ushortからsbyte,byte,short又はcharへの変換。
− intからsbyte,byte,short,ushort,uint,ulong又はcharへの変換。
− uintからsbyte,byte,short,ushort,int又はcharへの変換。
− longからsbyte,byte,short,ushort,int,uint,ulong又はcharへの変換。
− ulongからsbyte,byte,short,ushort,int,uint,long又はcharへの変換。
− charからsbyte,byte又はshortへの変換。
− floatからsbyte,byte,short,ushort,int,uint,long,ulong,char又はdecimalへ
の変換。
− doubleからsbyte,byte,short,ushort,int,uint,long,ulong,char,float又は
decimalへの変換。
− decimalからsbyte,byte,short,ushort,int,uint,long,ulong,char,float又は
doubleへの変換。
明示的な変換はすべての暗黙の数値変換及び明示的な数値変換を含むため,キャスト式(14.6.6参照)
を使うことで,任意の≪数値型≫から別の≪数値型≫に常に変換できる。
明示的な数値変換を行うと,情報が失われたり,例外が送出されたりする可能性がある。明示的な数値
変換は,次のように処理される。
150
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− ある整数型から別の整数型への変換について,処理は変換が行われるけた(桁)あふれ検査文脈(14.5.13
参照)に依存する。
・ checked文脈では,変換元演算対象の値が変換先の型の範囲内の場合に,変換が成功し,変換元演
算対象の値が変換先の型の範囲外の場合に,System.OverflowExceptionが送出される。
・ unchecked文脈では,変換は常に成功し,続けて次の処理が行われる。
− 変換元の型が変換先の型より大きい場合は,変換元の値から“余分な”上位ビットが破棄されて
切り捨てられる。その後,結果は変換先の型の値として扱われる。
− 変換元の型が変換先の型より小さい場合は,変換先の型と同じ大きさになるように,変換元の値
に対して符号拡張又はゼロ拡張が行われる。変換元の型が符号付きの場合は符号拡張が使われて,
符号なしの場合はゼロ拡張が使われる。その後,結果は変換先の型の値として扱われる。
− 変換元の型と変換先の型の大きさが同じ場合は,変換元の値は変換先の型の値として扱われる。
− decimalから整数型への変換では,変換元の値はゼロに向かって最も近い整数値に丸められて,この
整数値が変換の結果となる。結果の整数値が変換先の型の値域外の場合は,例外
System.OverflowExceptionが送出される。
− float又はdoubleから整数型への変換では,処理は変換が行われるけた(桁)あふれ検査文脈(14.5.13
参照)に依存する。
・ checked文脈では,変換は次のように行われる。
− 値がゼロ方向の最も近い整数値に丸められる。この整数値が変換先の型の値域内である場合は,
この値が変換の結果となる。
− それ以外の場合には,System.OverflowExceptionが送出される。
・ unchecked文脈では,変換は常に成功し,続けて次の処理が行われる。
− 値がゼロ方向の最も近い整数値に丸められる。この整数値が変換先の型の値域内である場合は,
この値が変換の結果となる。
− それ以外の場合は,変換先の型の未規定の値が変換の結果となる。
− doubleからfloatへの変換では,doubleの値は最も近いfloatの値に丸められる。この丸め処
理は,ゼロでない値を同じ符合のゼロに丸めてもよい。doubleの値の絶対値が大き過ぎてfloatで
は表現できない場合,結果は正の無限大又は負の無限大になる。doubleの値がNaN(非数値)の場
合,結果もNaNとなる。
− float又はdoubleからdecimalへの変換では,変換元の値はdecimal表現に変換され,必要で
あれば最も近い値に丸める(11.1.7参照)。この丸め処理は,ゼロでない値をゼロに丸めてもよい。変
換元の値の絶対値が大き過ぎてdecimalでは表現できない場合,又は,変換元の値がNaN又は無限
大の場合,System.OverflowExceptionを送出する。
− decimalからfloat又はdoubleへの変換では,decimalの値を最も近いdouble又はfloatの
値に丸める。変換しようとする値が変換先の型の範囲内にない場合,System.OverflowException
を送出する。
13.2.2 明示的な列挙変換
明示的な列挙変換は次とする。
− sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double又はdecimal
から任意の≪列挙型≫への変換。
− ≪列挙型≫からsbyte,byte,short,ushort,int,uint,long,ulong,char,float,double
151
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
又はdecimalへの変換。
− 任意の≪列挙型≫から他の任意の≪列挙型≫への変換。
二つの型の間の明示的な列挙変換は,関係するすべての≪列挙型≫を基礎になっている型として扱った
後,その結果の型の間で暗黙又は明示的な数値変換を実行することで処理される。
例 指定された≪列挙型≫ Eの基礎になっている型がintの場合,Eからbyteへの変換はintから
byteへの明示的な数値変換(13.2.1参照)として処理される。また,byteからEへの変換はbyte
からintへの暗黙の数値変換(13.1.2参照)として処理される。
13.2.3 明示的な参照変換
明示的な参照変換は次とする。
− objectから任意の≪参照型≫への変換。
− 任意の≪クラス型≫ Sから任意の≪クラス型≫ Tへの変換。ただし,SがTの基底クラスの場合とす
る。
− 任意の≪クラス型≫ Sから任意の≪インタフェース型≫ Tへの変換。ただし,Sが封印されておらず,
SがTを実装していない場合とする。
− 任意の≪インタフェース型≫ Sから任意の≪クラス型≫ Tへの変換。ただし,Tが封印されていない
か,TがSを実装する場合とする。
− 任意の≪インタフェース型≫ Sから任意の≪インタフェース型≫ Tへの変換。ただし,SがTから派
生しない場合とする。
− 要素型がSEの≪配列型≫ Sから要素型がTEの≪配列型≫ Tへの変換。ただし,次のすべての条件を
満たす場合とする。
・ S及びTは,要素型だけが異なる(すなわち,SとTが同じ次元数をもつ。)。
・ SEからTEへの明示的な参照変換が存在する。
− System.Array及びSystem.Arrayが実装しているインタフェースから任意の≪配列型≫への変換。
− System.Delegate及びSystem.Delegateが実装しているインタフェースから任意の≪委譲型≫
への変換。
− 1次元の≪配列型≫S[]からSystem.Collections.Generic.IList<T>及びその基底インタフェ
ースへの変換。ただし,SからTへの明示的な参照変換が存在する場合に限る。
− System.Collections.Generic.IList<T>及びその基底インタフェースから1次元の≪配列型≫
S[]への変換。ただし,S[]からSystem.Collections.Generic.IList<T>への暗黙的又は明示
的な参照変換が存在する場合に限る。これは,S若しくはTがいずれも同じ型になっているか,又は,
SからTへの暗黙的又は明示的な参照変換が存在する場合だけに限られる。
参照型(25.7参照)であることが分かっている≪型仮引数≫Tについては,次の明示的な参照変換が存
在する。
− Tの実効基底クラスCからTへの変換及びCの任意の基底クラスからTへの変換。
− 任意の≪インタフェース型≫からTへの変換。
− Tから任意の≪インタフェース型≫Iへの変換。ただし,TからIへの暗黙の参照変換が存在しない
場合に限る。
− ≪型仮引数≫UからTへの変換。ただし,TがUに依存する場合に限る(25.7参照)。
注記 Tは参照型であることが分かっているため,Tの有効範囲内では,たとえコンパイル時にU
が参照型になることが分かっていない場合でも,Uの実行時の型は常に参照型になる。
152
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
明示的な参照変換は≪参照型≫間の変換なので,正しいことを確認するために実行時検査が必要となる。
実行時に明示的な参照変換が成功するには,変換元演算対象の値はnullであるか,又は,変換元演算
対象で参照されているオブジェクトの実行時の型が暗黙の参照変換(13.1.4参照)によって変換先の型に
変換できる型でなければならない。明示的な参照変換が失敗した場合,
System.InvalidCastExceptionを送出する。
暗黙又は明示的な参照変換では,変換されるオブジェクトの参照識別性が変わることはない。
注記 すなわち,参照変換が参照の型を変更できても,参照されているオブジェクトの型や値が変化
することはない。
13.2.4 ボックス化解除変換
ボックス化解除変換は,object型又はSystem.ValueType型から任意の≪null許容でない値型≫
への明示的な変換,任意の≪インタフェース型≫からその≪インタフェース型≫を実装する任意の≪null
許容でない値型≫への明示的な変換及びSystem.Enumから任意の列挙型への明示的な変換を可能にする。
ボックス化解除の操作では,最初にオブジェクトインスタンスが,指定された≪値型≫又は列挙型をボッ
クス化した値であることを検査し,そのインスタンスの値を取り出し複写する。System.Enum型は列挙
型の直接の基底クラスであるため,System.Enum型から列挙型へのボックス化解除は可能とする(21.4
参照)。System.ValueType型はすべての構造体の直接の基底クラスであり,すべての列挙型の基底クラ
スであるため,System.ValueType型から構造体又は列挙型へのボックス化解除は可能とする(18.3.2
参照)。
ボックス化解除変換は,object若しくはSystem.ValueTypeから任意の≪null許容型≫への明示
的な変換又は任意の≪インタフェース型≫から基礎とする型がその≪インタフェース型≫を実装する≪
null許容型≫への明示的な変換,及び,System.Enumから基礎とする型が列挙型である任意の≪null
許容型≫への明示的な変換を可能とする。型Tの式eからnull許容型V?へのボックス化解除変換は次
のように処理する。
− eがnullの場合,結果は型V?のnull値とする。
− そうでなければ,結果はeからVにボックス化解除し,VからV?に包装(13.7参照)したものと同値
とする。
参照型であることが分かっていない≪型仮引数≫Tについては,コンパイル時には,Tを含む次の変換
をボックス化解除変換とみなす(25.7参照)。実行時にTが値型の場合,ボックス化解除変換とみなして
変換する。実行時にTが参照型の場合,明示的な参照変換又は恒等変換とみなして変換する。
− Tの実効基底クラスCからTへの変換及びCの任意の基底クラスからTへの変換。
注記 CはSystem.Object型,System.ValueType型,又はSystem.Enum型のいずれかで
ある。そうでなければ,Tは参照型となることが分かっているはずで,この節の規定では
なく13.2.3が適用される。
− 任意の≪インタフェース型≫からTへの変換。
ボックス化解除変換の他の規則については11.3.2で規定する。
13.2.5 明示的な型仮引数変換
13.2.5では明示的な参照変換又は明示的なボックス化解除変換に分類されない型仮引数に対する明示的
な変換について規定する。
参照型(25.7参照)であることが分かっていない≪型仮引数≫Tについて,次の明示的な変換が存在す
る。
153
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− Tから任意の≪インタフェース型≫Iへの変換。ただし,TからIへの暗黙の変換が存在しない場合
に限る。この変換は,Tからobjectへの暗黙のボックス化変換(13.1.5参照)と,それに続くobject
からIへの明示的な参照変換からなる。実行時にTが値型の場合,ボックス化変換とそれに続く明示
的な参照変換とみなして変換する。実行時にTが参照型の場合,明示的な参照変換として変換する。
− 型仮引数UからTへの変換。ただし,TがUに依存する場合に限る(25.7参照)。実行時にTが値型
であり,かつ,Uが参照型であった場合,ボックス化解除変換とみなして変換する。実行時にT及び
Uの両方が値型であった場合,T及びUは同じ型でなければならず,変換は行われない。実行時にT
が参照型であった場合,Uも参照型でなければならず,明示的な参照変換又は明示的な恒等変換とし
て変換を行う。
13.2.6 利用者定義の明示的な変換
利用者定義の明示的な変換は,省略可能な標準明示変換,利用者定義の暗黙又は明示的な変換演算子の
実行,別の省略可能な標準明示変換という順序で行われる。利用者定義の変換を評価する際の正確な規則
については13.4.4で規定する。
13.3 標準変換
標準変換は,利用者定義変換の一部として実行される可能性のある定義済みの変換とする。
13.3.1 標準暗黙変換
次の暗黙の変換は標準暗黙変換に分類される。
− 恒等変換(13.1.1参照)
− 暗黙の数値変換(13.1.2参照)
− 暗黙の参照変換(13.1.4参照)
− ボックス化変換(13.1.5参照)
− 暗黙の型仮引数変換(13.1.6参照)
− 暗黙の定数式変換(13.1.6参照)
− 暗黙のnull許容変換(13.7.2参照)
利用者定義の暗黙変換は,標準暗黙変換には含まれない。
13.3.2 標準明示変換
標準明示変換は,すべての標準暗黙変換に,逆方向の標準暗黙変換が存在する明示的な変換を加えたも
のとする。
注記 すなわち,型Aから型Bへの標準暗黙変換が存在する場合,型Aから型Bへの標準明示変換及
び型Bから型Aへの標準明示変換が存在する。
13.4 利用者定義変換
C#では,利用者定義変換を使って,定義済みの暗黙変換と明示的な変換を強化できる。利用者定義変換
は,クラス型又は構造体型の中に変換演算子(17.9.3参照)を宣言することで導入される。
13.4.1 宣言可能な利用者定義変換
− C#では,特定の利用者定義変換だけを宣言できる。既存の暗黙変換又は明示的な変換を再定義するこ
とはできない。
利用者定義変換に適用される制限については17.9.3で規定する。
13.4.2 利用者定義変換の評価
利用者定義変換は,値をその値の型,すなわち変換元の型から別の型,すなわち変換先の型に変換する。
利用者定義変換の評価は,特定の変換元の型と変換先の型に対する最も限定的な利用者定義変換演算子を
154
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
検出することを中心に行われる。この決定作業は,次の段階に分解される。
− 利用者定義変換演算子を考慮する基になるクラス及び構造体の集合を探す。この集合は,変換元の型
及びその基底クラス並びに変換先の型及びその基底クラスから成る。ここでは,クラス及び構造体だ
けが利用者定義型演算子を宣言でき,クラス以外の型には基底クラスがないことを暗黙に仮定する。
末尾に修飾子?が付いている場合,利用者定義変換演算子を考慮する基になる型の集合を決定する前に,
変換元及び変換先の型から?を取り除く。例えば,型S?から型T?への変換の場合,利用者定義変換演
算子の基になる型の集合は,SとTから成る。
− 発見された型の集合から,適用可能な利用者定義変換演算子を決定する。変換演算子が適用可能であ
るためには,変換元の型から演算子の演算対象の型への標準変換(13.3参照)を実行でき,演算子の
結果の型から変換先の型への標準変換を実行できなければならない。変換元の型及び変換先の型の両
方がnull許容の場合,適用可能な変換演算子は,利用者定義変換演算子だけではなく,もち上げら
れた変換演算子(13.7.3参照)も含む。適用可能な利用者定義変換演算子の集合が空の場合,変換元
の型から変換先の型への利用者定義変換は存在しない。
− 適用可能な利用者定義型演算子の集合から,どの演算子があいまいさなく最も限定的かを決定する。
分かりやすくいえば,最も限定的な演算子とは,演算対象の型が変換元の型に“最も近く”,結果の型
が変換先の型に“最も近い”演算子のことである。最も限定的な利用者定義変換演算子を確定する厳
密な規則については,13.4.3〜13.7.3で規定する。
多重定義解決のために,適用可能な利用者定義変換演算子の集合が空でない場合であって,かつ,その
場合に限り,変換元の型から変換先の型への利用者定義変換が存在する。適用可能な演算子の集合は空で
ないが,一意な最も限定的な演算子を含まない場合,たとえ変換を適用すると常にコンパイル時エラーに
なるとしても,利用者定義変換が存在するものとみなす。
最も限定的な利用者定義変換演算子を特定したら,利用者定義変換の実際の実行は,次の3段階で行う。
− 最初に,必要であれば,変換元の型から利用者定義変換演算子の演算対象の型への標準変換を実行す
る。
− 次に,利用者定義変換演算子を呼び出して変換を実行する。
− 最後に,必要であれば,利用者定義変換演算子の結果の型から変換先の型への標準変換を実行する。
利用者定義変換の評価に複数の利用者定義変換演算子が含まれることはない。すなわち,型Sから型T
への変換が,まずSからXへの利用者定義変換を実行した後,XからTへの利用者定義変換を実行すると
いう手順で行われることはない。
利用者定義の暗黙変換又は明示的な変換の評価の厳密な定義については,13.4.3及び13.4.4で規定する。
規定では次の用語を使用する。
− 型Aから型Bへの標準暗黙変換(13.3.1参照)が存在し,A及びBがいずれも≪インタフェース型≫
ではない場合,AはBに包含されるといい,BはAを包含するという。
− 型の集合の中で最も外側の型とは,集合内の他のすべての型を包含している唯一の型とする。他のす
べての型を包含する唯一の型がない場合,その集合は最も外側の型をもたない。より直感的な言葉で
表現すれば,最も外側の型とは集合の中で“最も大きい”型であり,他の各型はその型に暗黙に変換
できる。
− 型の集合の中で最も内側の型とは,集合内の他のすべての型に包含されている唯一の型とする。他の
すべての型に包含される唯一の型がない場合,その集合は最も内側の型をもたない。より直感的な言
葉で表現すれば,最も内側の型とは集合の中で“最も小さい”型であり,その型は他の各型に暗黙に
155
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
変換できる。
13.4.3 利用者定義の暗黙の変換
型Sから型Tへの利用者定義の暗黙の変換は次のように処理される。
− S及びTの後ろに修飾子?が付いている場合,S及びTから後続する修飾子?を取り除くことによって
得られる型S0及びT0を決定する。
− 利用者定義変換演算子を考慮する型の集合Dを探す。この集合は,S0(S0がクラス又は構造体の場
合),S0の基底クラス(S0がクラスの場合),S0の実効基底クラス及びその基底クラス(S0が型仮
引数の場合),並びに,T0(T0がクラス又は構造体の場合)から成る。
− 適用可能な利用者定義変換演算子の集合Uを探す。この集合は利用者定義の変換からなり,S及びT
の両方がnull許容型の場合には,Dの中のクラス又は構造体によって宣言された,Sを包含する型
からTに包含される型に変換するもち上げられた暗黙の変換演算子 (13.7.3) 群からなる。Uが空の場
合,変換は存在せず,コンパイル時エラーが起こる。
− 次の手順によって,Uの中の演算子の型のうち,最も限定的な元の型SXを探す。
・ Uの中の演算子のいずれかがSからの変換を行う場合,SをSXとする。
・ それ以外の場合,Uの中の演算子群の変換元の型の結合した集合の中で最も内側の型をSXとする。
最も内側の型が見つからない場合又は最も内側の型が一つに決まらない場合,その変換はあいまい
とし,コンパイル時エラーが起こる。
− 次の手順によって,Uの中の演算子の型のうち,最も限定的な変換先の型TXを探す。
・ Uの中の演算子のいずれかがTへの変換を行う場合,TをTXとする。
・ それ以外の場合,Uの中の演算子群の変換先の型の結合した集合の中で最も外側の型をTXとする。
最も外側の型が見つからない場合又は最も外側の型が一つに決まらない場合,その変換はあいまい
とし,コンパイル時エラーが起こる。
− 最も限定的な変換演算子を探す。
・ UがSXからTXへの利用者定義変換演算子を一つだけ含む場合,その演算子を最も限定的な変換演
算子とする。
・ そうでない場合であって,UがSXからTXへのもち上げられた変換演算子を正確に一つだけ含む場
合,その演算子を最も限定的な変換演算子とする。
・ そうでなければ,変換はあいまいとし,コンパイル時エラーになる。
− 最後に,次の手順で変換を適用する。
・ SがSXでない場合,SからSXへの標準暗黙変換を実行する。
・ SXからTXに変換するために最も限定的な変換演算子を実行する。
・ TXがTでない場合,TXからTへの標準暗黙変換を実行する。
13.4.4 利用者定義の明示的な変換
型Sから型Tへの利用者定義の明示的な変換は次のように処理される。
− S及びTの後ろに修飾子?が付いている場合,S及びTから後続する修飾子?を取り除くことによって
得られる型S0及びT0を決定する。
− 利用者定義変換演算子を考慮する型の集合Dを探す。この集合は,S0(S0がクラス又は構造体の場
合),S0の基底クラス(S0がクラスの場合),S0の実効基底クラス及びその基底クラス(S0が型仮
引数の場合),T0(T0がクラス又は構造体の場合),T0の基底クラス(T0がクラスの場合),並びに
T0の実効基底クラス及びその基底クラス(T0が型仮引数の場合)から成る。
156
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 適用可能な変換演算子の集合Uを探す。この集合は,Dの中のクラス又は構造体によって宣言された,
Sを包含する型若しくはSによって包含される型からTを包含する型若しくはTに包含される型への
利用者定義の暗黙の変換演算子又は利用者定義の明示的な変換演算子,並びに,S及びTの両方が
null許容の場合,もち上げられた暗黙又は明示的な変換演算子から成る(13.7.3参照)。Uが空の場
合,変換は存在せず,コンパイル時エラーが起こるものとする。
− 次の手順によって,Uの中の演算子の型のうち,最も限定的な元の型SXを探す。
・ Uの中の演算子のいずれかがSからの変換を行う場合,SをSXとする。
・ それ以外の場合で,Uの中の演算子のいずれかがSを包含する型からの変換を行う場合,これらの
演算子の元の型の結合した集合の中で最も内側の型をSXとする。最も内側の型が見つからない場合
又は最も内側の型が一つに決まらない場合,その変換はあいまいとし,コンパイル時エラーが起こ
るものとする。
・ それ以外の場合,Uの中の演算子の元の型の結合した集合の中で最も外側の型をSXとする。最も外
側の型が見つからない場合又は最も外側の型が一つに決まらない場合,その変換はあいまいとし,
コンパイル時エラーが起こるものとする。
− 次の手順によって,Uの中の演算子の変換先の型のうち,最も限定的な変換先の型TXを探す。
・ Uの中にTへの変換を行う演算子が存在する場合,TをTXとする。
・ それ以外の場合で,Uの中にTに包含される型への変換が存在する場合,それらの演算子の変換先
の型の結合した集合の中で最も外側の型をTXとする。最も外側の型が見つからない場合又は最も外
側の型が一つに決まらない場合,その変換はあいまいとし,コンパイル時エラーが起こるものとす
る。
・ それ以外の場合,Uの中の演算子の変換先の型の結合した集合のうち最も内側の型をTXとする。最
も内側の型が見つからない場合又は最も内側の型が一つに決まらない場合,その変換はあいまいと
し,コンパイル時エラーが起こるものとする。
− 最も限定的な変換演算子を探す。
・ UがSXからTXへの変換を行う利用者定義変換演算子を一つだけ含む場合,その演算子を最も限定
的な変換演算子とする。
・ それ以外の場合で,UがSXからTXに変換するもち上げられた変換演算子を一つだけ含む場合,そ
れを最も限定的な変換演算子とする。
・ それ以外の場合,変換はあいまいとし,コンパイル時エラーが起こるものとする。
− 最後に,変換を適用する。
・ SがSXでない場合,SからSXへの標準明示変換を実行する。
・ SXからTXへの変換を行う最も限定的な変換演算子を実行する。
・ TXがTでない場合,TXからTへの標準明示変換を実行する。
13.5 無名メソッド変換
≪無名メソッド式≫(14.5.15参照)から任意の互換性のある委譲型への暗黙の変換(13.1参照)が存在
する。Dが委譲型,かつ,Aが≪無名メソッド式≫の場合,次の二つの条件が満足する場合であって,か
つ,その場合に限り,DはAと互換性がある。
− まず,Dの仮引数の型がAの引数の型と互換でなければならない。
・ Aが≪無名メソッド呼出し情報≫を含まず,Dのどの仮引数も仮引数修飾子outで修飾されていな
い場合,Dは0個以上の任意の型の仮引数をもつことができる。
157
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
・ Aが≪無名メソッド呼出し情報≫をもつ場合,Dは同数の仮引数をもっていなければならず,Aの
各仮引数はDの対応する仮引数と互換でなければならない。Aの仮引数とDの仮引数が同じ型であ
り,Aの仮引数の修飾子ref又はoutの有無がDの対応する仮引数のものと一致している場合に,
Aの仮引数とDの仮引数が互換であるとみなされる。AとDの互換性を判定する際,Dの最後の仮
引数が≪仮引数配列≫であるかどうかは考慮されない。≪仮引数配列≫修飾子をもつ仮引数と≪仮
引数配列≫修飾子をもたない仮引数が同じ型の場合,それらは互換とする。
− 次に,Dの返却値の型がAの返却値の型と互換でなければならない。次の二つの規則に対しては,A
は他の無名メソッドの≪ブロック≫を含んでいるとはみなされない。
・ Dの返却値の型がvoidと宣言されている場合,Aが含むすべてのreturn文に式を指定してはな
らない。
・ Dの返却値の型がRと宣言されている場合,Aが含むすべてのreturn文に暗黙的にRに変換可能
(13.1参照)な式を指定しなければならない。したがって,Aの≪ブロック≫の終了点は到達可能
ではない。
注記 ≪無名メソッド式≫の≪ブロック≫は,たとえそれが到達不能な文に含まれているとして
も,常に到達可能とみなされる。15.1参照。
互換性のある委譲型への暗黙の変換のほかに,≪無名メソッド式≫からの変換が存在しない。これは
object型への変換でさえ同様とする。
例 次の例はこれらの例を説明するものである。
delegate void D(int x);
D d1 = delegate { };
// Ok
D d2 = delegate() { };
// Error, signature mismatch
D d3 = delegate(long x) { };
// Error, signature mismatch
D d4 = delegate(int x) { };
// Ok
D d5 = delegate(int x) { return; };
// Ok
D d6 = delegate(int x) { return x; }; // Error, return type
mismatch
delegate void E(out int x);
E e1 = delegate { };
// Error, E has an out
parameter
E e2 = delegate(out int x) { x = 1; }; // Ok
E e3 = delegate(ref int x) { x = 1; }; // Error, signature mismatch
delegate int P(params int[] a);
P p1 = delegate { };
// Error, end of block
reachable
P p2 = delegate { return; };
// Error, return type
mismatch
P p3 = delegate { return 1; };
// Ok
P p4 = delegate { return "Hello"; }; // Error, return type
mismatch
P p5 = delegate(int[] a) {
// Ok
158
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
return a[0];
};
P p6 = delegate(params int[] a) {
// Error, params modifier
return a[0];
};
P p7 = delegate(int[] a) {
// Error, return type
mismatch
if (a.Length > 0) return a[0];
return "Hello";
};
delegate object Q(params int[] a);
Q q1 = delegate(int[] a) {
// Ok
if (a.Length > 0) return a[0];
return "Hello";
};
≪委譲生成式≫(14.5.10.3参照)は,無名メソッドを委譲型に変換するための代替文法として使用する
ことができる。
13.6 メソッドグループ変換
13.5で説明した暗黙の無名メソッド変換と同様に,メソッドグループ(14.1参照)から互換な委譲型へ
の暗黙の変換が存在する。Dが委譲型で,Eがメソッドグループに分類される式の場合,Eが,その正常
形式(14.4.2.1参照)の中にDの仮引数の型及び修飾子と一致する型及び修飾子をもつ任意の実引数並び
(14.4.1) に適用可能な少なくとも一つのメソッドを含む場合,かつ,その場合に限り,DはEと互換とす
る。
コンパイル時に適用するEからDへの変換は,コンパイル時の委譲生成式new D(E)(14.5.10.3参照)
の処理と同じとする。EからDへの暗黙の変換の存在は,適用可能なメソッドの集合が空ではないことを
示すだけで,コンパイル時にその変換の適用がエラーを起こさず成功することを保証するものではないこ
とに注意する。
例 次のコードについて考える。
using System;
using System.Windows.Forms;
class AlertDialog
{
Label message = new Label();
Button okButton = new Button();
Button cancelButton = new Button();
public AlertDialog() {
okButton.Click += new EventHandler(OkClick);
cancelButton.Click += new EventHandler(CancelClick);
…
}
159
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void OkClick(object sender, EventArgs e) {
…
}
void CancelClick(object sender, EventArgs e) {
…
}
}
上記のコードにおいて,構築子は,new演算子を使って二つの委譲インスタンスを生成してい
る。この処理は,暗黙のメソッドグループ変換を使うことによって,次のように短く記述するこ
とができる。
public AlertDialog() {
okButton.Click += OkClick;
cancelButton.Click += CancelClick;
…
}
他のすべての暗黙の変換及び明示的変換と同様に,キャスト演算子を使うことによって,明示的に特定
の変換を実行することができる。
例
object obj = new EventHandler(myDialog.OkClick);
この文は,次のように書き換えることができる。
object obj = (EventHandler)myDialog.OkClick;
メソッドグループ及び無名メソッド式は,多重定義解決には影響を与える可能性があるが,型推論(25.6.4
参照)には関与しない。
13.7 null許容型を伴う変換
13.7.1〜13.7.3では,次の用語を使用する。
− 包装するという用語は,T型の値をT?型のインスタンスで包む処理を意味する。T型の値xをT?型
に包装する処理は,式new T?(x)を評価することによって行われる。
− 包装解除するという用語は,T?型のインスタンス内に保持されているT型の値を取り出す処理を意味
する。T?型の値xをT型に包装解除する処理は,式x.Valueを評価することで行われる。nullの
インスタンスを包装解除しようとすると,System.InvalidOperationExceptionが送出される
13.7.1 null型変換
null型(11.2.7参照)から任意のnull許容型への暗黙の変換が存在する。この変換は指定されたnull
許容型のnull値 (12.2) を生成する。
13.7.2 null許容変換
null許容変換は,null許容でない型を操作するあらかじめ定義された変換が,null許容でない値型
をそのnull許容形式で使用できるようする。null許容でない値型Sからnull許容でない値型Tへの
変換(13.1.1,13.1.2,13.1.3,13.1.7,13.2.1及び13.2.2参照)を行うあらかじめ定義された暗黙の変換又
は明示的な変換のそれぞれについて,次のnull許容変換が存在する。
− S?からT?への暗黙の変換及び明示的な変換
− SからT?への暗黙の変換及び明示的な変換
160
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− S?からTへの明示的な変換
null許容変換は,それ自身,暗黙の変換又は明示的な変換に分類される。
ある種のnull許容変換は,標準変換に分類され,利用者定義変換の一部として実行されることがある。
特に,すべての暗黙のnull許容変換は標準暗黙変換(13.3.1参照)に分類され,13.3.2の要件を満たす明
示的なnull許容変換は標準明示変換に分類される。
SからTへの基礎とする変換に基づくnull許容変換の評価は次のように実行される。
− 実行するnull許容変換がS?からT?への変換の場合
・ 変換元の値がnull(HasValue特性が偽)の場合,変換結果はT?型の値nullとする。
・ そうでなければ,その変換は,S?からSに包装解除,基礎とする変換であるSからTへの変換,T
からT?に包装の順に評価する。
− 実行するnull許容変換がSからT?への変換の場合,基礎とする変換であるSからTへの変換,T
からT?に包装の順に評価する。
− 実行するnull許容変換がS?からTへの変換の場合,S?からSに包装解除,基礎とする変換であるS
からTへの変換の順に評価する。
13.7.3 もち上げられた変換
null許容でない値型Sからnull許容でない値型Tへの変換を行う利用者定義変換演算子について,
S?からT?へのもち上げられた変換演算子が存在する。このもち上げられた変換演算子は,S?からSに包
装解除,SからTへの利用者定義変換,TからT?に包装の順に実行される。ただし,S?の値がnullのと
きは,T?のnull値に直接変換する。
もち上げられた変換演算子は,その基礎とする利用者定義変換演算子が暗黙の変換であれば,もち上げ
られた変換演算子も暗黙の変換に分類され,明示的な変換であれば,もち上げられた変換演算子も明示的
な変換に分類される。
14 式
式は,一連の演算子及び演算対象からなる。箇条14では,式の構文,演算対象及び演算子の評価順序,
並びに,式の意味を定義する。
14.1 式の分類
式は,次のいずれかに分類される。
− 値。すべての値には,型が関連付けられる。
− 変数。すべての変数には,型すなわち変数の宣言された型が関連付けられる。
− 名前空間。この分類による式は,≪メンバアクセス≫(14.5.4参照)の左辺としてだけ表現できる。
それ以外の文脈で,名前空間に分類される式を使用すると,コンパイル時エラーになる。
− 型。この分類による式は,≪メンバアクセス≫(14.5.4参照)の左辺としてだけ表現できる。それ以
外の文脈で,型に分類される式を使用すると,コンパイル時エラーになる。
− メソッド群。これは,メンバ検索(14.3参照)の結果得られる多重定義メソッドの集合とする。メソ
ッド群には,インスタンス式が関連付けられる場合がある。インスタンスメソッドが呼ばれたとき,
インスタンス式の評価結果は,this(14.5.7参照)で表されたインスタンスとなる。メソッド群は,
≪呼出し式≫(14.5.5参照)又は≪委譲生成式≫(14.5.10.3参照),又は互換性のある委譲型に暗黙の
変換でだけ許される。それ以外の文脈で,メソッド群に分類される式を使用すると,コンパイル時エ
ラーになる。
161
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 無名メソッド。この分類による式は,≪委譲生成式≫(14.5.10.3参照)又は互換性のある委譲型に暗
黙の変換でだけ許される。それ以外の文脈で,無名メソッドに分類される式を使用すると,コンパイ
ル時エラーになる。
− 特性アクセス。すべての特性アクセスには,型すなわち特性の型が関連付けられる。さらに,特性ア
クセスには,インスタンス式が関連付けられる場合がある。インスタンス特性にアクセスするアクセ
ス子(getブロック又はsetブロック)が呼ばれるとき,インスタンス式の評価結果は,this(14.5.7
参照)によって表されたインスタンスとなる。
− イベントアクセス。すべてのイベントアクセスには,型すなわちイベントの型が関連付けられる。さ
らに,イベントアクセスには,インスタンス式が関連付けられる場合がある。イベントアクセスは,
演算子+=及び演算子-=の左辺演算対象として使用できる(14.14.3参照)。それ以外の文脈で,イベン
トアクセスに分類される式を使用すると,コンパイル時エラーになる。
− 添字子アクセス。すべての添字子アクセスには,型すなわち添字子の要素型が関連付けられる。さら
に,添字子アクセスには,インスタンス式及び実引数並びが関連付けられている場合がある。インス
タンス特性にアクセスするアクセス子(getブロック又はsetブロック)が呼ばれるとき,インスタ
ンス式の評価結果は,this(14.5.7参照)によって表されたインスタンスとなり,実引数並びの評価
結果は,呼出しの仮引数並びとなる。
− なし。式が,返却型voidのメソッド呼出しであるときに発生する。なしと分類された式は,≪式文
≫(15.6参照)の文脈においてだけ有効とする。
式の最終的な結果が,名前空間,型,メソッド群,無名メソッド又はイベントアクセスになることは決
してない。上で示したとおり,これらの種類の式は,中間的な構築要素で,特定の文脈でだけ許される。
特性アクセス又は添字子アクセスは,≪getアクセス子≫又は≪setアクセス子≫の呼出し実行によっ
て,常に値として分類し直される。具体的なアクセス子は,特性又は添字子のアクセス文脈によって決定
される。すなわち,そのアクセスが代入の対象であれば,≪setアクセス子≫が,新しい値を割り当てる
ために(14.14.1参照)呼び出される。それ以外の場合は,≪getアクセス子≫が,現在の値を取得するた
めに(14.1.1参照)呼び出される。
14.1.1 式の値
式を含むほとんどの構成要素は,その式が最終的には値となることを必要とする。そのような場合に,
実際の式が,名前空間,型,メソッド群又は“なし”になると,コンパイル時エラーになる。しかし,式
が特性アクセス,添字子アクセス又は変数を表す場合は,その特性,添字子又は変数の値は,次のように
暗黙に置換される。
− 変数の値は,単純に,変数が示す記憶域に現在格納されている値とする。変数は,その値を得る前に,
確実に代入されているとみなされなければならない(12.3参照)。そうでなければ,コンパイル時エラ
ーになる。
− 特性アクセス式の値は,特性の≪getアクセス子≫を呼び出して取得する。特性に≪getアクセス子
≫がない場合は,コンパイル時エラーになる。そうでなければ,関数メンバの呼出し(14.4.3参照)
が実行され,呼出しの結果が,特性アクセスの値となる。
− 添字子アクセスの値は,添字子の≪getアクセス子≫を呼び出して取得する。添字子に≪getアクセ
ス子≫がない場合は,コンパイル時エラーになる。そうでなければ,関数メンバの呼出し(14.4.3参
照)が,添字子アクセス式及び実引数並びによって実行され,呼出しの結果が,添字子アクセス式の
値となる。
162
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
14.2 演算子
式は,演算対象及び演算子で構成される。式の演算子は,演算対象に適用する演算を示す。
例 演算子の例には,+,-,*,/及びnewがある。演算対象の例には,リテラル,フィールド,局
所変数及び式がある。
演算子には,次の3種類がある。
− 単項演算子。単項演算子は,一つの演算対象をもち,前置表記法(‒xなど)又は後置表記法(x++な
ど)のいずれかを使用する。
− 2項演算子。2項演算子は,二つの演算対象をもち,中置記法(x + yなど)を使用する。
− 3項演算子。3項演算子?:が,唯一存在する。それは,三つの演算対象をもち中置記法(c ? x : y)
を使用する。
式中の演算子の評価順序は,演算子の優先順位及び結合規則によって決定する(14.2.1参照)。
式における演算子の評価順序は,左から右とする。
例 F(i) + G(i++) * H(i)において,メソッドFは,iの旧値を用いて呼ばれる,次に,メソッ
ドGが,iの旧値を用いて呼ばれる,最後にメソッドHが,iの新値を用いて呼ばれる。この順
序は,演算子の優先順位とは独立で無関係となる。
ある種の演算子は,多重定義できる。演算子の多重定義によって,演算対象の少なくとも一つが利用者
定義されたクラス又は利用者定義された構造体型の演算に対して,利用者定義の演算子実装を規定できる
(14.2.2参照)。
14.2.1 演算子の優先順位及び結合規則
式が複数個の演算子を含むときは,演算子の優先順位が個々の演算子の評価順序を制御する。
注記 例えば,式x + y * zでは,演算子*が,2項演算子+より優先順位が高いので,x + (y * z)
と評価する。
演算子の優先順位は,その文法の生成規則の定義によって確立される。
注記 例えば,加減式は,演算子+又は演算子‒によって分離された一連の≪乗除式≫として構成され
る。このように,演算子+及び演算子‒は,演算子*,演算子/及び演算子%より優先順位が低い。
次の表に,演算子を優先順位の高いものから低いものの順に示す。
節
分類
演算子
14.5
一次式
x.y f(x) a[x] x++ x-- new
typeof checked unchecked
14.6
単項式
+ - ! ~ ++x --x (T)x
14.7
乗除式
* / %
14.7
加減式
+ -
14.8
シフト
<< >>
14.9
関係及び型検査
< > <= >= is as
14.9
等価性
== !=
14.10
論理積
&
14.10
排他的論理和
^
14.10
論理和
|
14.11
二択条件論理積
&&
14.11
二択条件論理和
||
14.12
null判定選択
??
14.13
二択条件
?:
14.14
代入演算子
= *= /= %= += -= <<= >>= &= ^= |=
163
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
同じ優先順位の二つの演算子の間に演算対象がある場合は,次に述べる演算子の結合規則によって,演
算の実行順序を制御する。
− 代入演算子及びnull判定選択演算子を除くすべての2項演算子の結合規則は,左結合とする。すな
わち,演算は左から右に実行される。
例 x + y + zは,(x + y) + zと評価される。
− 代入演算子,null判定選択演算子及び二択条件演算子(?:)の結合規則は,右結合とする。すなわ
ち,演算は右から左に実行される。
例 x = y = zは,x = (y = z)と評価される。
優先順位及び結合規則は,括弧を用いて制御できる。
例 x + y * zは,最初に,yにzを乗じ,その結果にxを加える。しかし,(x + y) * z
は,最初にxとyとを加え,その結果に,zを乗じる。
14.2.2 演算子多重定義
すべての単項演算子及び2項演算子には,任意の式で自動的に使用できるあらかじめ定義された実装が
ある。定義済みの実装に加えて,利用者定義の実装を,クラス及び構造体中のoperator宣言(17.9参照)
に導入できる。利用者定義演算子実装は,常にあらかじめ定義された演算子実装よりも優先順序が高い。
適用可能な利用者定義演算子実装が存在しないときにだけ,あらかじめ定義された演算子実装が,14.2.3
及び14.2.4に示すように考慮される。
多重定義可能な単項演算子は次とする。
+ - ! ~ ++ -- true false
注記 true及びfalseは,式中に明示的に使われない(したがって,14.2.1の優先順位表にも含ま
れていない。)。しかし,それらは,真理式(14.17参照),並びに,二択条件演算子(14.13参照)
及び二択条件論理演算子(14.11参照)を含む式などの幾つかの式文脈において呼び出されるの
で,演算子とみなされる。
多重定義可能な2項演算子は次とする。
+ - * / % & | ^ << >> == != > < >= <=
ここに挙げた演算子だけが多重定義可能とする。具体的には,メンバアクセス,メソッド呼出し,又は,
=,&&,||,?:,checked,unchecked,new,typeof,as及びisの演算子は,多重定義できない。
2項演算子が多重定義されるとき,対応する代入演算子があれば,それも暗黙に多重定義される。
例 演算子*の多重定義は,演算子*=の多重定義でもある。これについては,更に14.14で示す。
代入演算子自身(=)は,多重定義できない。代入では,常に,値の単純なビットごとのコピーが変数
に格納される。
(T)xのようなキャスト操作は,利用者定義変換を与えることによって多重定義される(13.4参照)。
a[x]のような要素アクセスは,多重定義可能な演算子とはみなされない。その代わりに,利用者定義の
添字指定が,添字子(17.8参照)に提供されている。
演算子は,式において演算子記法を,宣言において関数記法を使って参照される。次の表は,単項演算
子及び2項演算子について,演算子記法と関数記法との関係をまとめている。1行目のopは,多重定義で
きる単項前置演算子を表す。2行目のopは,単項後置演算子++及び単項後置演算子--を表す。3行目の
opは,多重定義できる2項演算子を表す。
例 演算子++及び演算子--の多重定義は,17.9.1を参照。
164
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
演算子記法
関数記法
op x
operator op(x)
x op
operator op(x)
x op y
operator op(x, y)
利用者定義演算子の宣言では,常に,少なくとも一つの仮引数で,その演算子の宣言を含むクラス型又
は構造体型を指定する必要がある。
注記 したがって,利用者定義演算子があらかじめ定義された演算子と同じ呼出し情報をもつことは
できない。
利用者定義演算子の宣言では,演算子の構文,優先順位又は結合規則を変更することはできない。
例 演算子/は,常に2項演算子であり,14.2.1で示す優先順位をもち,常に左結合とする。
注記 利用者定義演算子では,どのような計算でも実行できるが,直感的に予想されるものと異なる
結果を生成するような実装は避けることを強く推奨する。例えば,演算子==の実装は,等価の
ための二つの演算対象の比較及び適切な結果boolの返却にすることを推奨する。
14.5〜14.14での各演算子の記述は,演算子のあらかじめ定義された実装及び各演算子に適用される任意
の追加的規則を規定する。記述中では,単項演算子多重定義解決,2項演算子多重定義解決及び数値昇格
という用語を使う。14.2.3〜14.2.6では,これらの用語の定義を示す。
14.2.3 単項演算子多重定義解決
opが多重定義可能な単項演算子であり,xが型Xの式のとき,op x 又はx opの演算は,次のとおり処
理される。
− 演算operator op(x)に対してXが提供する利用者定義演算子候補の集合は,14.2.5の規則を用いて
決定される。
− 利用者定義演算子の候補集合が空でない場合は,それが,この演算に対する演算子候補集合になる。
空の場合は,あらかじめ定義された単項演算子operator opの実装が,この演算に対する演算子候
補集合となる。型Xが列挙型でなければ,仮引数型が列挙型のあらかじめ定義された単項演算子は,
候補から取り除かれる。演算子のあらかじめ定義された実装は,演算子の記述(14.5及び14.6参照)
において規定する。
− 14.4.2の多重定義解決規則を演算子候補集合に適用して,実引数並び(x)に関して最良の演算子を選
択する。この演算子が,多重定義解決の結果となる。多重定義解決が,単一の最適な演算子を選択で
きない場合は,コンパイル時エラーになる。
14.2.4 2項演算子多重定義解決
opが多重定義可能な2項演算子であり,xが型Xの式及びyが型Yの式のとき,x op yの演算は,次
のとおり処理される。
− 演算operator op(x, y)に対してX及びYが提供する利用者定義演算子候補の集合を決定する。こ
の集合は,14.2.5の規則を用いて,Xが提供する演算子候補及びYが提供する演算子候補の集合和と
なる。もし,X及びYが同じ型であるか,X及びYが共通の基底型から派生するのであれば,共有さ
れた演算子候補は,その和集合において一つの集合だけ存在する。
− 利用者定義演算子の候補集合が空でない場合は,これがその演算に対する演算子候補の集合となる。
空の場合は,あらかじめ定義された2項演算子operator opの実装が,その演算に対する演算子候
補集合になる。XもYも列挙型でなければ,仮引数型が列挙型のあらかじめ定義された2項演算子は,
165
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
候補から取り除かれる。同様に,XもYも委譲型でなければ,仮引数型が委譲型のあらかじめ定義さ
れた2項演算子は,候補から取り除かれる。演算子のあらかじめ定義された実装は,演算子の記述(14.7
〜14.13参照)において規定する。
− 14.4.2の多重定義解決規則を演算子候補集合に適用して,実引数並び(x, y)に関して最良の演算子を
選択する。この演算子が,多重定義解決の結果となる。多重定義解決が,単一の最適な演算子を選択
できない場合は,コンパイル時エラーになる。
14.2.5 利用者定義演算子候補
多重定義可能な演算子op及び実引数並びAにおいて,型T及び演算operator op(A)が与えられたと
き,operator op(A)に対するTが提供する利用者定義演算子候補集合は,次のように決定される。
− Tから,?修飾子があれば,それを削除し,それを型T0とする。
− T0におけるすべての宣言operator opについて,少なくとも一つの演算子が実引数並びAに関して
適用可能(14.4.2.1参照)ならば,演算子候補の集合は,T0におけるすべての適用可能な宣言operator
opから構成される。T0で宣言された演算子のもち上げ形式も,T0によって宣言されたものとみなさ
れる。
− その他の場合で,T0がobjectならば,演算子候補集合は,空となる。
− その他の場合,T0が提供する演算子候補集合は,T0の直接的な基底クラスが提供する演算子候補集
合,又はT0が型仮引数の場合,T0の実効基底クラスが提供する演算子候補集合となる。
14.2.6 数値昇格
14.2.6は,参考とする。
数値昇格では,あらかじめ定義された単項数値演算子及び2項数値演算子の演算対象に対して暗黙変換
が自動的に行われる。数値昇格は,独立した機構ではなく,あらかじめ定義された演算子に対する多重定
義解決適用による効果である。数値昇格は,利用者定義演算子の評価には具体的な影響を及ぼさないが,
同様の効果を示すように利用者定義演算子を実装できる。
数値昇格の例として,2項演算子*のあらかじめ定義された実装を考える。
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
void operator *(long x, ulong y);
void operator *(ulong x, long y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);
多重定義解決規則(14.4.2参照)が,この演算子集合に適用されると,演算対象の型の間で暗黙変換の
存在する演算子がまず選択される。
例 例えば演算b * sのbがbyteでsがshortの場合,多重定義解決は,最良の演算子として
operator *(int, int)を選択する。このように,b及びsは,intに変換され,結果の型とし
てintが選択される。同様に,演算i * dのiがintで,dがdoubleの場合,多重定義解決
は,最良の演算子としてoperator *(double, double)を選択する。
166
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
14.2.6.1 単項数値昇格
14.2.6.1は,参考とする。
あらかじめ定義された単項演算子+,単項演算子‒及び単項演算子~の演算対象に対して,単項数値昇格
が発生する。単項数値昇格では,sbyte,byte,short,ushort,又はcharの各型の演算対象をint
型に変換する。さらに,単項演算子‒に対しては,単項数値昇格は,uint型の演算対象をlong型に変換
する。
14.2.6.2 2項数値昇格
14.2.6.2は,参考とする。
あらかじめ定義された+,‒,*,/,%,&,|,^,==,!=,>,<,>=及び<=という2項演算子に対し
て,2項数値昇格が発生する。2項数値昇格は,両方の演算対象を共通の型に変換し,関係演算子以外につ
いては,それを演算の結果の型にする。2項数値昇格では,次の規則がこの順序で適用される。
− いずれかの演算対象がdecimal型ならば,もう一方の演算対象もdecimal型に変換される。もう一
方の演算対象がfloat又はdouble型ならば,コンパイル時エラーになる。
− その他の場合で,いずれかの演算対象がdouble型ならば,もう一方の演算対象もdouble型に変換
される。
− その他の場合で,いずれかの演算対象がfloat型ならば,もう一方の演算対象もfloat型に変換さ
れる。
− その他の場合で,いずれかの演算対象がulong型ならば,もう一方の演算対象もulong型に変換さ
れる。もう一方の演算対象がsbyte,short又はlong型ならば,コンパイル時エラーになる。
− その他の場合で,いずれかの演算対象がlong型ならば,もう一方の演算対象もlong型に変換され
る。
− その他の場合で,いずれかの演算対象がuint型で,もう一方の演算対象が,sbyte,short,又は
int型ならば,両方の演算対象はlong型に変換される。
− その他の場合で,いずれかの演算対象がuint型ならば,もう一方の演算対象もuint型に変換され
る。
− そうでなければ,両方の演算対象がint型に変換される。
注記1 最初の規則は,decimal型とdouble型及びfloat型とが混在するあらゆる演算を禁止す
る。この規則は,decimal型とdouble型及びfloat型との間には,暗黙の変換がないと
いう事実からくる。
注記2 また,一方の演算対象が,符号付き整数型の場合,もう片方がulong型であることはないこ
とに注意する。これは,符号付き整数型と同時にulongの全範囲を表現できるような整数型
が存在しないためである。
上のいずれの場合も,キャスト式を使って,一方の演算対象をもう一方の演算対象と互換性のある型に
明示的に変換できる。
例
decimal AddPercent(decimal x, double percent) {
return x * (1.0 + percent / 100.0);
}
decimal型に,double型を乗算することはできないためにコンパイル時エラーになる。この
エラーは,2番目の演算対象をdecimal型に明示的に変換することで解決できる。次に例を示す。
167
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
decimal AddPercent(decimal x, double percent) {
return x * (decimal)(1.0 + percent / 100.0);
}
14.2.7 もち上げ演算子
もち上げ演算子は,null許容でない値型の演算を行うあらかじめ定義された演算子及び利用者定義演
算子を,それらの型のnull許容形式でも利用できるようにする。もち上げ演算子は,次に示したある要
件にあるあらかじめ定義された演算子及び利用者定義演算子から構築される。
− 次の単項演算子に対して,演算対象の型及び返却値の型がいずれもnull許容でない値型であれば,
もち上げ形式が存在する。
+ ++ - -- ! ~
もち上げ形式は,演算対象の型及び返却型に単一の?修飾子を付与することで構築される。演算対
象がnullの場合,もち上げ演算子はnull値を生成する。演算対象がnullでなければ,もち上げ
演算子は演算対象を包装解除し,基礎となる演算子を適用し,その結果を包装する。
− 次の2項演算子に対して,演算対象の型及び返却値の型のすべてがnull許容でない値型であれば,
もち上げ形式が存在する。
+ - * / % & | ^ << >>
もち上げ形式は,個々の演算対象の型及び返却型に単一の?修飾子を付与することで構築される。
一方又は両方の演算対象がnullの場合,もち上げ演算子はnull値を生成する。ただし,14.10.4に
示したとおり,bool?型に対する&演算子及び|演算子は除く。演算対象がnullでなければ,もち上
げ演算子は演算対象を包装解除し,基礎となる演算子を適用し,その結果を包装する。
− 次の等価演算子に対して,両方の演算対象の型がnull許容でない値型であって,返却型がboolの
場合,もち上げ形式が存在する。
+ - * / % & | ^ << >>
もち上げ形式は,個々の演算対象の型に単一の?修飾子を付与することで構築される。二つのnull
値は等しいとみなし,null値はnullでない値とは等しくないとみなす。両方の演算対象がnull
でなければ,もち上げ演算子は演算対象を包装解除し,基礎となる演算子を適用し,bool値の結果
を生成する。
− 次の関係演算子に対して,両方の演算対象の型がnull許容でない値型であって,返却型がboolの
場合,もち上げ形式が存在する。
+ - * / % & | ^ << >>
もち上げ形式は,個々の演算対象の型に単一の?修飾子を付与することで構築される。一方又は両
方の演算対象がnullの場合,もち上げ演算子はfalseを生成する。そうでなければ,もち上げ演算
子は演算対象を包装解除し,基礎となる演算子を適用し,bool値の結果を生成する。
あらかじめ定義された演算子のもち上げ形式は,それ自身,あらかじめ定義された演算子とみなされる。
14.3 メンバ検索
メンバ検索は,型の文脈における名前の意味を決定するための処理である。メンバ検索は,式中の≪単
純名≫(14.5.2参照)又は≪メンバアクセス≫(14.5.4参照)の評価の一部として行われる場合がある
メンバ検索は,メンバ名だけでなく,そのメンバがもつかたかり引数の数及び,そのメンバがアクセス
可能かどうかについても考慮される。メンバ検索のために,総称メソッド及ぶ入れ子になった総称型は,
168
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
それらの宣言内で指示された型仮引数の数をもち,それ以外のメンバは0個の型仮引数をもつものとする。
型Tにおいて,K個の型仮引数をもつ名前Nのメンバ検索は,次のように処理される。
− 最初に,Nという名前のアクセス可能なメンバの集合を決定する。
・ Tが型仮引数であれば,その集合は,Tの一次制約又は二次制約(25.7参照)として指定された個々
の型において,Nという名前のアクセス可能なすべてのメンバの集合の和集合とする。これには,
object内のNという名前のアクセス可能なメンバの集合も含まれる。
・ そうでなければ,その集合は,T内のNという名前のアクセス可能な(10.5参照)メンバすべて,
継承したメンバでアクセス可能なメンバすべて及び,object内のNという名前のアクセス可能性
メンバで構築する。Tが構築型の場合,メンバの集合は,25.5.4で示したとおり型実引数で置換す
ることで得られる。修飾子overrideを含む宣言は,その集合から除外する。
− 次に,アクセス可能なメンバの集合が空集合であれば,検索の結果は不一致となり,以降の手順は評
価されない。
− 次に,Kが0個であれば,宣言に型仮引数を含んだ入れ子型のすべてを,その集合から取り除く。K
が0個でなければ,型仮引数の数が異なるメンバのすべてを,その集合から取り除く。Kが0個のと
き,型仮引数をもつメソッドすべては,その集合から取り除かない。なぜなら,型推論の過程で,型
実引数を推論できるからである。
− 次に,他のメンバによって隠ぺいされているメンバを集合から取り除く。この段階の間で取り除かれ
たメンバは,依然として他のメンバを取り除く理由になる。Sは,メンバMが宣言されている型とし,
集合内のすべてのメンバS.Mに対して,次の規則を適用する。
・ Mが,定数,フィールド,特性,イベント,列挙メンバ又は型宣言のいずれかである場合,Sの基
底型で宣言されるすべてのメンバを集合から取り除く。
・ Mがメソッドの場合,Sの基底型で宣言されるメソッド以外のすべてのメンバを集合から取り除く。
− 次に,クラスメンバによって隠ぺいされているインタフェースメンバを集合から取り除く。Tが型仮
引数であって,Tがobject以外の実効基底クラス及び空でない実効インタフェース集合(25.7参照)
の両方をもつ場合にだけ,この段階が作用する。Sは,メンバMが宣言されている型とし,集合内の
すべてのメンバS.Mに対して,Sがobject以外のクラス宣言である場合,次の規則を適用する。
・ Mが,定数,フィールド,特性,イベント,列挙メンバ又は型宣言のいずれかである場合,インタ
フェース宣言内で宣言されるすべてのメンバを集合から取り除く。
・ Mがメソッドの場合,インタフェース宣言インタフェースで宣言されるメソッド以外のすべてのメ
ンバを集合から取り除く。
− 最後に,隠ぺいされたメンバを取り除いて,検索の結果を次のとおり決定する。
・ 集合に含まれているのが,メソッド以外のメンバ一つだけである場合は,そのメンバが検索の結果
となる。
・ そうでなくて,集合に含まれているのがメソッドだけの場合は,そのメソッド群が検索の結果とな
る。
・ そうでなければ,検索結果はあいまいであり,コンパイル時エラーになる。
型仮引数及びインタフェースを除くの型におけるメンバ検索,並びに,厳密な単一継承インタフェース
(継承連鎖内の各インタフェースが厳密に0個又は1個の直接基底インタフェースをもつ場合)における
メンバ検索では,検索規則を実施した結果,派生メンバによって同じ名前をもつ基底メンバが隠ぺいされ
る。このような単一継承の検索は,あいまいになることはない。多重継承インタフェースでのメンバ検索
169
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
で発生する可能性のあるあいまいさについては,20.2.5で述べる。
14.3.1 基底型
メンバ検索では,型Tは次の基底型をもつとみなす。
− Tがobjectの場合,Tは基底型をもたない。
− Tが≪列挙型≫の場合,Tの基底型は,クラス型System.Enum,System.ValueType及びobject
となる。
− Tが≪構造体型≫の場合,Tの基底型は,クラス型System.ValueType及びobjectとなる。
− Tが≪クラス型≫の場合,Tの基底型は,クラス型objectを含むTの基底クラスとなる。
− Tが≪インタフェース型≫の場合,Tの基底型は,Tの基底インタフェース及びクラス型objectと
なる。
− Tが≪配列型≫の場合,Tの基底型は,クラス型System.Array及びobjectとなる。
− Tが≪委譲型≫の場合,Tの基底型は,クラス型System.Delegate及びobjectとなる。
− Tが≪null許容型≫の場合,Tの基底型は,クラス型System.ValueType及びobjectとなる。
14.4 関数メンバ
関数メンバは,実行可能な文をもつメンバとする。関数メンバは,常に型のメンバであり,名前空間の
メンバになることはできない。C#では,次の種類の関数メンバを定義する。
− メソッド
− 特性
− イベント
− 添字子
− 利用者定義演算子
− インスタンス構築子
− 静的構築子
− 終了化子
明示的に呼び出すことができない終了化子及び静的構築子を除いて,関数メンバがもつ文は関数メンバ
呼出しを通じて実行される。関数メンバ呼出しを記述するための実際の構文は,その関数メンバの種類に
よる。
関数メンバ呼出しの実引数並び(14.4.1参照)は,関数メンバの仮引数に実際の値又は変数参照を与え
る。
メソッド,添字子,演算子及びインスタンス構築子の呼出しでは,多重定義解決を用いて,関数メンバ
の候補から呼び出すメンバを決定する。この処理については,14.4.2で述べる。
多重定義解決を行うなどして,コンパイル時に関数メンバを特定した後の,関数メンバ呼出しの実行時
処理については,14.4.3に示す。
注記 次の表は,明示的に呼び出すことができる関数メンバの6種類の構成要素で行われる処理をま
とめる。この表では,e,x,y,及びvalueは,変数又は値として分類される式を,Tは型に
分類される式を,Fはメソッドの単純名を,そしてPは特性の単純名をそれぞれ表す。
170
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
構築要素
例
説明
メソッド呼出し
F(x, y)
多重定義解決を適用して,このFの呼出しを含むクラス又は構造体内の最
良メソッドFを選択する。メソッドは,実引数並び(x, y)によって呼び
出される。メソッドが,staticでない場合,インスタンス式はthisとな
る。
T.F(x, y)
多重定義解決を適用して,クラス又は構造体Tの最良メソッドFを選択す
る。メソッドが,staticでない場合,コンパイル時エラーになる。メソ
ッドは,実引数並び(x, y)で呼び出される。
e.F(x, y)
多重定義解決を適用して,eの型で与えられるクラス,構造体又はインタ
フェースの最良メソッドFを選択する。メソッドが,staticの場合,コ
ンパイル時エラーになる。インスタンス式e及び実引数並び(x, y)で呼
び出される。
特性アクセス
P
この特性Pを含むクラス又は構造体内の特性Pのgetアクセス子が呼び出
される。Pが書出し専用の場合,コンパイル時エラーになる。Pがstatic
でない場合,インスタンス式はthisである。
P = value
この特性Pを含むクラス又は構造体内の特性Pのsetアクセス子が,実引
数並び(value)で呼び出される。Pがstaticでない場合,インスタンス
式はthisである。
T.P
クラス又は構造体T内の特性Pのgetアクセス子が呼び出される。Pが
staticでない場合又はPが書出し専用の場合,コンパイル時エラーにな
る。
T.P = value
クラス又は構造体T内の特性Pのsetアクセス子が,実引数並び(value)
で呼び出される。Pがstaticでない場合又はPが読込み専用の場合,コ
ンパイル時エラーになる。
e.P
eの型で与えられるクラス,構造体又はインタフェース内の特性Pのget
アクセス子が,インスタンス式eで呼び出される。Pがstaticの場合又
はPが書出し専用の場合,コンパイル時エラーになる。
e.P = value
eの型で与えられるクラス,構造体又はインタフェース内の特性Pのset
アクセス子が,インスタンス式e及び実引数並び(value)で呼び出される。
Pがstaticの場合又はPが読込み専用の場合,コンパイル時エラーにな
る。
イベントアクセス
E += value
このイベントEを含むクラス又は構造体内のイベントEのaddアクセス子
が呼び出される。Eがstaticでない場合,インスタンス式はthisであ
る。
E -= value
このイベントEを含むクラス又は構造体内のイベントEのremoveアクセ
ス子が呼び出される。Eがstaticでない場合,インスタンス式はthis
である。
T.E += value
クラス又は構造体T内のイベントEのaddアクセス子が呼び出される。E
がstaticでない場合,コンパイル時エラーになる。
T.E -= value
クラス又は構造体T内のイベントEのremoveアクセス子が呼び出される。
Eがstaticでない場合,コンパイル時エラーになる。
e.E += value
Eの型で与えられるクラス,構造体又はインタフェース内のイベントEの
addアクセス子が,インスタンス式eで呼び出される。Eがstaticの場
合,コンパイル時エラーになる。
e.E -= value
Eの型で与えられるクラス,構造体又はインタフェース内のイベントEの
removeアクセス子が,インスタンス式eで呼び出される。Eがstatic
の場合,コンパイル時エラーになる。
添字子アクセス
e[x, y]
多重定義解決を適用して,eの型で与えられるクラス,構造体又はインタ
フェース内の最良の添字子を選択する。添字子のgetアクセス子が,イン
スタンス式e及び実引数並び(x, y)で呼び出される。添字子が書出し専用
の場合,コンパイル時エラーになる。
171
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
構築要素
例
説明
e[x, y] = value 多重定義解決を適用して,eの型で与えられるクラス,構造体又はインタ
フェース内の最良の添字子を選択する。添字子のsetアクセス子がインス
タンス式e及び実引数並び(x, y, value)で呼び出される。添字子が読
込み専用の場合,コンパイル時エラーになる。
演算子呼出し
-x
多重定義解決を適用して,xの型で与えられるクラス又は構造体内の最良
の単項演算子を選択する。選択された演算子は,実引数並び(x)で呼び出さ
れる。
x + y
多重定義解決を適用して,X及びyの型で与えられるクラス又は構造体内
の最良の2項演算子を選択する。選択された演算子は,実引数並び(x, y)
で呼び出される。
インスタンス構築
子呼出し
new T(x, y)
多重定義解決を適用する,クラス又は構造体T内の最良のインスタンス構
築子を選択する。インスタンス構築子は,実引数並び(x, y)で呼び出され
る。
14.4.1 実引数並び
すべての関数メンバ呼出しには,関数メンバの仮引数に対する実際の値又は変数参照を渡す実引数並び
が含まれる。関数メンバ呼出しの実引数並びを指定するための構文は,関数メンバの種類に依存する。
− インスタンス構築子,メソッド及び委譲の場合の実引数は,次で規定するとおり≪実引数並び≫とし
て指定する。
− 特性の場合の実引数並びは,getアクセス子を呼び出すときは空で,setアクセス子を呼び出すとき
は代入演算子の右辺演算対象として指定された式で構成される。
− イベントの場合の実引数並びは,演算子+=又は演算子-=の右辺演算対象として指定された式で構成さ
れる。
− 添字子の場合の実引数並びは,添字子アクセスの角括弧の間に指定した式で構成される。setアクセ
ス子を呼び出すときは,代入演算子の右演算対象として指定された式が,実引数並びに更に追加され
る。
注記 追加される実引数は,多重定義解決には利用されず,setアクセス子の呼出しの間でだけ
使われる。
− 利用者定義演算子の場合の実引数並びは,単項演算子の一つの演算対象,又は2項演算子の二つの演
算対象で構成される。
特性(17.6参照),イベント(17.7参照)及び利用者定義演算子(17.9参照)の実引数は,常に値仮引数
(17.5.1.1参照)として渡される。添字子(17.8参照)の実引数は,常に値仮引数(17.5.1.1参照)又は仮
引数配列(17.5.1.4参照)として渡される。これらの種類の関数メンバでは,参照仮引数及び出力仮引数
は使われない。
インスタンス構築子,メソッド又は委譲の呼出しの実引数は,≪実引数並び≫として指定される。
≪実引数並び≫:
≪実引数≫
≪実引数並び≫ , ≪実引数≫
≪実引数≫:
≪式≫
ref ≪変数参照≫
out ≪変数参照≫
172
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪実引数並び≫は,コンマで区切られた一つ以上の≪実引数≫で構成する。各実引数は,次のいずれか
の形式を使用する。
− ≪式≫は引数が値仮引数(17.5.1.1参照)として渡されることを示す。
− キーワードrefに≪変数参照≫(12.4参照)が続く。引数が参照仮引数(17.5.1.2参照)として渡さ
れることを示す。変数は,参照仮引数として渡される前に,確実に代入されていなければならない。
− キーワードoutに≪変数参照≫(12.4参照)が続く。引数が出力仮引数(17.5.1.3参照)として渡さ
れることを示す。変数は,出力仮引数として渡される関数メンバの呼出しの後では,確実に代入され
ている(12.3参照)とみなされる。
揮発性フィールド(17.4.3参照)は,参照仮引数又は出力仮引数として渡すと,警告される。なぜなら,
揮発性フィールドは呼び出されたメソッドでは揮発性として扱えないからである。
関数メンバ呼出し(14.4.3参照)の実行処理中は,実引数並びの式及び変数参照は,次に示すように左
から右という順に評価される。
− 値仮引数の場合,実引数式が評価されて,対応する仮引数型への暗黙の変換(13.1参照)が実行され
る。結果の値が,関数メンバ呼出しにおける値仮引数の初期値になる。
− 参照仮引数又は出力仮引数の場合,変数参照が評価されて,結果の記憶域が,関数メンバ呼出しの仮
引数で表現される記憶域になる。参照仮引数又は出力仮引数として与えられた変数参照が参照型の配
列要素である場合は,配列の要素型と仮引数の型とが同一であることを確認するために,実行時検査
が実行される。この検査が失敗すると,System.ArrayTypeMismatchExceptionが送出される。
メソッド,添字子及びインスタンス構築子は,右端の仮引数を仮引数配列(17.5.1.4参照)として宣言で
きる。このような関数メンバは,標準形式又は展開形式のいずれが適用できる(14.4.2.1参照)かに応じ
て,いずれかの形式で呼び出される。
− 仮引数配列をもつ関数メンバが標準形式で呼び出されるときは,仮引数配列で指定される実引数は,
仮引数配列の型に暗黙で変換できる(13.1参照)型の単一の式である必要がある。この場合,仮引数
配列は値仮引数と全く同じように機能する。
− 仮引数配列をもつ関数メンバが展開形式で呼び出されるときは,仮引数配列に対する0個以上の実引
数を呼出しで指定しなければならない。各実引数は,仮引数配列の要素型に暗黙で変換できる(13.1
参照)型の式である。この場合,呼出しによって,実引数の個数に対応した長さをもつ仮引数配列型
のインスタンスが生成され,その配列インスタンスの要素が指定の実引数値で初期化され,新しく生
成された配列インスタンスが実際の実引数として使用される。
実引数並びの式は,常に記述された順序で評価される。
例 次に例を示す。
class Test
{
static void F(int x, int y, int z) {
System.Console.WriteLine("x = {0}, y = {1}, z = {2}", x, y,
z);
}
static void Main() {
int i = 0;
F(i++, i++, i++);
173
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
この例は,次のように出力する。
x = 0, y = 1, z = 2
配列の共変性規則(19.5参照)によって,BからAへの暗黙変換が存在する限り,配列型A[]の値は配
列型B[]のインスタンスへの参照とすることができる。この規則によって,≪参照型≫の配列要素が,参
照仮引数又は出力仮引数として渡されたとき,配列の実際の要素型が,その仮引数の型と一致することを
保証する実行時検査を必要とする。
例
class Test
{
static void F(ref object x) {…}
static void Main() {
object[] a = new object[10];
object[] b = new string[10];
F(ref a[0]);
// Ok
F(ref b[1]);
// ArrayTypeMismatchException
}
}
Fの2番目の呼出しでは,実際の要素型がstringであってobjectでないので,
System.ArrayTypeMismatchExceptionが送出される。
仮引数配列をもつ関数メンバが,拡張形式で呼ばれるとき,その呼出しは,配列初期化子(14.5.10.2参
照)を含む配列生成式が,拡張された仮引数の周りに挿入されているかのように処理される。
例 次の宣言を考える。
void F(int x, int y, params object[] args);
次に,メソッドを展開形式で呼び出す例を示す。
F(10, 20);
F(10, 20, 30, 40);
F(10, 20, 1, "hello", 3.0);
上の呼出しは,次の呼出しに厳密に対応する。
F(10, 20, new object[] {});
F(10, 20, new object[] {30, 40});
F(10, 20, new object[] {1, "hello", 3.0});
特に,仮引数配列の引数がゼロ個のとき空配列が作成されることに注意する。
14.4.2 多重定義解決
多重定義解決とは,実引数並び及び関数メンバ候補集合に基づいて,呼び出すべき最良の関数メンバを
選択するコンパイル時機構とする。多重定義解決によって,C#の次のような異なる文脈において,呼び出
すべき関数メンバが選択される。
− ≪呼出し式≫(14.5.5参照)で指定されたメソッド呼出し。
− ≪オブジェクト生成式≫(14.5.10.1参照)で指定されたインスタンス構築子呼出し。
174
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− ≪要素アクセス≫(14.5.6参照)を通しての添字子アクセス子呼出し。
− 式(14.2.3及び14.2.4参照)で参照されるあらかじめ定義された演算子又は利用者定義演算子の呼出
し。
これらの各文脈は,関数メンバの候補集合及び実引数並びを独自の方法で定義する。しかし,関数メン
バ候補及び実引数並びがいったん決定した後で,最良の関数メンバを選択する方法は,すべての文脈で同
じとなる。
− 最初に,与えられた実引数並び(14.4.2.1参照)に適用可能な関数メンバ候補集合に,絞り込む。絞り
込んだ集合が空の場合,コンパイル時エラーとなる。
− 次に,関数メンバ候補集合の中から最良の関数メンバを特定する。集合に含まれている関数メンバが
一つだけの場合は,それが最良の関数メンバとなる。複数の関数メンバが含まれている場合は,14.4.2.2
の規則を使って関数メンバを相互に比較し,指定されている実引数並びに関して他のすべての関数メ
ンバより適切な関数メンバが,最良の関数メンバになる。他のすべての関数メンバより適切な関数メ
ンバを一つだけに絞り込むことができない場合,関数メンバの呼出しはあいまいであり,コンパイル
時エラーになる。
14.4.2.1〜14.4.2.3では,適用可能な関数メンバ及びより適切な関数メンバという用語の正確な意味を定
義する。
14.4.2.1 適用可能な関数メンバ
関数メンバは,次のすべてが真である場合に,実引数並びAに関して適用可能な関数メンバという。
− Aの実引数の個数が,関数メンバの宣言における仮引数の個数と一致する。
− Aの各実引数について,実引数の渡し方(すなわち,値,ref又はout)が,対応する仮引数の渡し
方と同じであって,かつ,次のいずれかが成立する。
・ 値仮引数又は仮引数配列の場合は,実引数の型から対応する仮引数の型への暗黙の型変換(13.1参
照)が存在する。
・ ref仮引数又はout仮引数の場合,実引数の型は対応する仮引数の型と一致する。
注記 結局,ref仮引数又はout仮引数は,渡される実引数の別名となる。
仮引数配列を含む関数メンバについて,上の規則が適用可能である場合は,標準形式で適用可能という。
仮引数配列を含む関数メンバが,標準形式で適用可能でない場合でも,展開形式で適用可能である場合が
ある。
− 展開形式は,実引数並びAの実引数の個数が仮引数の総数と一致するように,関数メンバ宣言の仮引
数配列を,仮引数配列の要素型の0個以上の値仮引数で,置き換えて構築する。Aの実引数の個数が
関数メンバ宣言の固定仮引数の個数より少ない場合は,関数メンバの展開形式を構築できず,したが
って適用可能ではない。
− それ以外で,Aの各実引数について,実引数の渡し方が対応する仮引数の渡し方と一致し,かつ,次
のいずれかが成立する場合に,展開形式が適用可能とする。
・ 固定値仮引数又は拡張によって作成された値仮引数の場合は,実引数の型から対応する仮引数の型
への暗黙の変換(13.1参照)が存在する。
・ ref仮引数又はout仮引数の場合は,引数の型は,対応する仮引数の型と一致する。
14.4.2.2 より適切な関数メンバ
実引数型の順序列{A1,A2,…,AN}をもつ実引数並びA,並びに,仮引数型{P1,P2,…,PN}及び{Q1,
Q2,…,QN}をもつ二つの適用可能な関数メンバMP及びMQがあるとすると,展開及び型実引数の置換を行
175
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
った後,次の条件のときMPは,MQに比べて,より適切な関数メンバであると定義される。
− 各実引数について,AXからPXへの暗黙の変換は,AXからQXへの暗黙の変換より不適切ではない。
− 少なくとも一つの引数について,AXからPXへの変換は,AXからQXへの変換より適切である。
この評価を行うとき,MP又はMQが拡張形式でだけ適用可能ならば,PX又はQXは,仮引数並びの拡張形
式の仮引数を参照する。
拡張された仮引数型{P1,P2,…,PN}及び{Q1,Q2,…,QN}が同一の場合,関数メンバMP及びMQに,
それぞれ指定された初期化されておらず,かつ,拡張されていない仮引数型{R1,R2,…,RK}と{S1,S2,
…,SL}とを比較して,より適切関数メンバを決定するために,次のようなタイブレーク規則が適用され
る。この場合,次の規則に従って,より適切な関数メンバが決定される。
− MP及びMQの一方が総称でなく,もう一方が総称であれば,総称でいないほうがより適切とする。
− そうでない場合であって,MP及びMQの一方が展開されていない形式(又は,仮引数配列をもたない)
であり,もう一方が展開形式(及び,仮引数配列をもつ)でだけ適用可能である場合,展開させてい
ない形式がより適切とする。
− そうでない場合であって,MPの仮引数の個数KとMQの仮引数の個数Lが異なる場合,より多くの仮
引数をもつメソッドがより適切とする。両方のメソッドが仮引数配列をもち,展開形式で適応可能で
ある場合にだけ,この条件が発生する。
− そうでない場合であって,MPの仮引数の個数KとMQの仮引数の個数Lが同じであり,一方のメソッ
ドがより特殊性が高い仮引数型をもつ場合,そのメソッドがより適切とする。指定された仮引数型{R1,
R2,…,RK}及び{S1,S2,…,SL}において,個々の仮引数RxがSxより特殊性が低いわけではなく,
少なくとも一つRxがSxより特殊性が高い場合,{R1,R2,…,RK}は{S1,S2,…,SL}より特殊性が
高いと定義する。型仮引数は,型仮引数でない仮引数よりも特殊性は低い。再帰的に,同じ数の型実
引数をもつ二つの構築型において,一方の構築型の型実引数の少なくとも一つが,他方の構築型での
対応する型実引数より特殊性が高く,より特殊性が高い型実引数が他方での対応する型実引数になけ
れば,一方の構築型は,他方の構築型より特殊性が高い。同じ次元をもつ二つの配列型において,一
方の配列の要素型が,他方の配列型の要素型より特殊性が高い場合,一方の配列型は他方の配列型よ
り特殊性が高い。
− そうでない場合であって,あるメンバがもち上げ演算子であり,別のメンバがもち上げられていない
演算子の場合,もち上げられていない演算子がより適切とする。
− そうでない場合は,より適切なメソッドはない。
14.4.2.3 より適切な変換
型Sから型T1への変換を行う暗黙の変換C1及び型Sから型T2への変換を行う暗黙の変換C2があると
すると,これらの二つの変換のうち,より適切な変換は,次のとおりに決定される。
− T1及びT2が,同じ型である場合,いずれも,より適切ではない。
− SがT1の場合,C1がより適切な変換とする。
− SがT2の場合,C2がより適切な変換とする。
− T1からT2への暗黙の変換が存在して,T2からT1への暗黙の変換が存在しない場合,C1がより適切な
変換とする。
− T2からT1への暗黙の変換が存在して,T1からT2への暗黙の変換が存在しない場合,C2がより適切な
変換とする。
− T1がsbyteで,T2がbyte,ushort,uint又はulongである場合,C1がより適切な変換とする。
176
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− T2がsbyteで,T1がbyte,ushort,uint又はulongである場合,C2がより適切な変換とする。
− T1がshortで,T2がushort,uint又はulongである場合,C1がより適切な変換とする。
− T2がshortで,T1がushort,uint又はulongである場合,C2がより適切な変換とする。
− T1がintで,T2がuint又はulongである場合,C1がより適切な変換とする。
− T2がintで,T1がuint又はulongである場合,C2がより適切な変換とする。
− T1がlongでT2がulongである場合,C1がより適切な変換とする。
− T2がlongでT1がulongである場合,C2がより適切な変換とする。
− それ以外の場合は,いずれの変換も,より適切ではない。
これらの規則によって,暗黙の変換C1が,暗黙の変換C2より適切な変換として定義されるならば,C2
は,C1に比べてより不適切な変換ということもできる。
14.4.3 関数メンバ呼出し
14.4.3は,関数メンバを呼び出すために実行時に行われる処理について規定する。コンパイル時の処理
において,関数メンバ候補集合に多重定義解決を適用するなどして,呼び出す特定のメンバが既に決定さ
れているとする。
呼出し処理を説明するために,関数メンバを次の二つに分類する。
− 静的関数メンバ。静的メソッド,静的特性アクセス子及び利用者定義演算子である。静的関数メンバ
は,常に非仮想である。
− インスタンス関数メンバ。インスタンスメソッド,インスタンス構築子,インスタンス特性アクセス
子及び添字子アクセス子である。インスタンス関数メンバは非仮想又は仮想のいずれかで,必ず特定
のインスタンスで呼び出される。インスタンスは,インスタンス式によって計算され,関数メンバの
中では,this(14.5.7参照)としてアクセス可能となる。インスタンス構築子に対して,新たにオブ
ジェクトを割り当てるためにインスタンス式が使われる。
関数メンバ呼出しの実行時処理は,次の手順で構成される。Mは関数メンバとする。もしMがインスタ
ンスメンバならば,Eはインスタンス式とする。
− Mが関数メンバの場合
・ 実引数並びは,14.4.1のとおり評価される。
・ Mが呼び出される。
− Mが,≪値型≫で宣言される関数メンバの場合
・ Eが評価される。この評価で例外が発生すると,これ以降の手順は実行されない。インスタンス構
築に対して,この評価は,新しいオブジェクトに対する記憶域を(通常,実行スタックから)割り
当てることで構成される。この場合,Eは変数として分類される。
・ Eが変数に分類されないならば,Eの型で一時的な局所変数が生成され,Eの値が,この変数に割
り当てられる。そして,Eは,その一時局所変数に対する参照として再分類される。この一時変数
は,Mの中では,thisとしてアクセスできるが,他の方法ではアクセスできない。したがって,E
が真の変数のときだけ,呼出し側は,Mがthisに対して行った変更を観察できる。
・ 実引数並びは,14.4.1のとおり評価される。
・ Mが呼び出される。Eによって参照される変数が,thisによって参照される変数となる。
− Mが≪参照型≫で宣言されるインスタンス関数メンバの場合
・ Eが評価される。この評価で例外が発生すると,これ以降の手順は実行されない。インスタンス構
築に対して,この評価は,新しいオブジェクトに対する記憶域を(通常,GCヒープから)割り当て
177
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ることで構成される。
・ 実引数並びは,14.4.1のとおり評価される。
・ Eの値が有効か検査される。Eの型が≪参照型≫であって,Eの値がnullならば,
System.NullReferenceExceptionが送出され,以降の手順は,実行されない。
・ 呼び出す関数メンバの実装を決定する。
− Eのコンパイル時の型がインタフェースであるならば,呼び出す関数メンバはEで参照されるイ
ンスタンスの実行時の型が提供するMの実装となる。この関数メンバはインタフェース写像規則
(20.4.2参照)を適用して決定される。
− そうでなければ,Mが仮想関数メンバならば,呼び出す関数メンバは,Eによって参照されるイ
ンスタンスの実行時の型が提供するMの実装となる。この関数メンバは,Eによって参照される
インスタンスの実行時の型に関してMの最派生実装を決定する規則(17.5.3参照)を適用するこ
とによって決定される。
− そうでなければ,Mは非仮想関数メンバであり,呼び出す関数メンバは,M自身となる。
上の手順で決定された関数メンバの実装が呼び出される。Eの型が≪値型≫ならば,Eで参照されてい
る変数が,thisで参照される変数となる。そうでなければ,Eの型が≪参照型≫であり,Eで参照される
オブジェクトはthisで参照されるオブジェクトとなる。
注記 Eの型が≪値型≫ならば,Mが≪参照型≫で宣言されたインスタンスメンバ関数であっても,
ボックス化は発生しない。
14.4.3.1 ボックス化されたインスタンスの呼出し
≪値型≫で実装された関数メンバは,次の状況では,その≪値型≫のボックス化されたインスタンスを
通じて呼び出すことができる。
− 関数メンバが,型objectから継承したメソッドのoverride(上書き)であり,型objectのイン
スタンス式を通じて呼び出されるとき。
− 関数メンバが,インタフェース関数メンバの実装であり,≪インタフェース型≫のインスタンス式を
通じて呼び出されるとき。
− 関数メンバが,委譲を通じて呼び出されるとき。
これらの状況では,ボックス化されたインスタンスは≪値型≫の変数を含むものとみなされ,この変数
が,関数メンバ呼出しの中のthisによって参照される変数になる。
注記 これは,関数メンバがボックス化されたインスタンスに対して呼び出されたとき,関数メンバ
が,ボックス化されたインスタンスの値を変更できることを意味する。
14.5 一次式
一次式では,式の最も簡単な形式が使われる。
≪一次式≫:
≪配列生成式≫
≪一次非配列生成式≫
≪一次非配列生成式≫:
≪リテラル≫
≪単純名≫
≪括弧付き式≫
≪メンバアクセス≫
178
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪呼出し式≫
≪要素アクセス≫
≪thisアクセス≫
≪baseアクセス≫
≪後置増加式≫
≪後置減少式≫
≪オブジェクト生成式≫
≪委譲生成式≫
≪typeof式≫
≪checked式≫
≪unchecked式≫
≪省略時値式≫
≪無名メソッド式≫
一次式は,≪配列生成式≫及び≪一次非配列生成式≫に大きく分けられる。≪配列生成式≫をこのよう
に分離して扱うことで,他の単純な式の形式とともに記述する場合と比べて,次のような,関係者を混乱
させる可能性のあるコードを,文法的に禁止できる。
object o = new int[3][1];
分離して扱われていなければ,上のコードは次のように誤って解釈される可能性がある。
object o = (new int[3])[1];
≪一次式≫が,E.I形式の≪メンバアクセス≫(14.5.4参照)内Eであれば,≪一次式≫は静的クラス
(17.1.1.3参照)を参照することができる。
14.5.1 リテラル
≪リテラル≫(9.4.4参照)で構成される≪一次式≫は,値として分類される。
≪リテラル≫:
≪真理値リテラル≫
≪整数リテラル≫
≪実数リテラル≫
≪文字リテラル≫
≪文字列リテラル≫
≪nullリテラル≫
14.5.2 単純名
≪単純名≫は,一つの識別子,省略可能な型実引数で構成される。
≪単純名≫:
≪識別子≫ ≪型実引数並び≫opt
≪単純名≫は,Iを単一の識別子及び,<A1, ..., Ak>を省略可能な≪型実引数並び≫としたとき,形
式I又は形式I<A1, ..., Ak>のいずれかとする。≪型実引数並び≫が指定されていない場合,kを0と
みなす。
≪単純名≫は,次のとおりに評価分類される。
− kが0であって,≪単純名≫が≪ブロック≫の中で使われていて,その≪ブロック≫(又は外側のブ
ロック)の局所変数宣言空間(10.3参照)に名前Iの局所変数,仮引数又は定数が含まれている場合,
179
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪単純名≫は,その局所変数,仮引数又は定数を示し,変数又は値として分類される。
− kが0であって,≪単純名≫が総称メソッド宣言の本体で使われていて,その宣言に名前Iの型仮引
数を含んでいる場合,≪単純名≫は,その型仮引数を参照する。
− それ以外の場合,それぞれのインスタンス型Tについて,すぐ外側の型宣言から開始し,順番に外側
のクラス又は構造体の宣言(ある場合)のインスタンス型へと進みながら,次の処理を行う。
・ kが0であって,Tの宣言が名前Iの型仮引数をもつ場合,≪単純名≫は,その型仮引数を参照す
る。
・ そうでない場合であって,K個の型仮引数をもつT内のIのメンバ検索を
− Tがすぐ外側のクラス又は構造体型のインスタンス型であり,検索において,一つ以上のメソッ
ドが一致したら,結果は,関連付けられたインスタンス式thisのメソッド群とする。型実引数並
びが指定されていた場合,それは総称メソッドの呼出しで使われる。
− それ以外の場合であって,Tがすぐ外側のクラス又は構造体型のインスタンス型であって,検索
で一つのインスタンスメンバが一致して,インスタンス構築子,インスタンスメソッド又はイン
スタンスアクセス子の≪ブロック≫内で参照が行われるならば,結果は,形式this.Iのメンバ
アクセス(14.5.4参照)と同じになる。これは,kが0のときにだけ発生する。
− それ以外の場合,形式T.I又は形式T.I<A1, ..., Ak>のメンバアクセス(14.5.4参照)と同じ
とする。この場合,≪単純名≫がインスタンスメンバを参照するならば,コンパイル時エラーに
なる。
− それ以外の場合は,個々の名前空間Nについて,≪単純名≫が存在する名前空間から開始して,外側
にある各名前空間(存在する場合)を経て,大域的名前空間まで,実体が見つかるまで次の手順を行
う。
・ kが0であって,IがN内の名前空間の名前であれば,次の処理を行う。
− ≪単純名≫が使われている場所がNの名前空間宣言で囲まれていて,その名前空間宣言が,名前
Iに名前空間又は型に関連付ける≪外部別名指令≫及び≪using別名指令≫を含む場合,≪単純
名≫は,あいまいであり,コンパイル時エラーになる。
− そうでなければ,≪単純名≫はN内の名前Iの名前空間を参照する。
・ それ以外の場合で,あって,Nが名前I及びk個の型仮引数をもつアクセス可能な型を含む場合,
次の処理を行う。
− kが0であって,≪単純名≫が使われている場所がNの名前空間宣言で囲まれていて,その名前
空間宣言が,名前Iに名前空間又は型に関連付ける≪外部別名指令≫及び≪using別名指令≫を
含む場合,≪単純名≫はあいまいであり,コンパイル時エラーになる。
− そうでなければ,≪単純名≫は,指定された型実引数で構築された型を参照する。
− それ以外の場合であって,≪単純名≫が使われている場所がNの名前空間宣言で囲まれている場
合,次の処理を行う。
− kが0であって,その名前空間宣言が,名前Iに移入される名前空間又は型に関連付ける≪外部
別名指令≫及び≪using別名指令≫を含む場合,≪単純名≫は名前空間又は型を参照する。
− そうでない場合であって,≪using名前空間指令≫によって移入される名前空間が,名前I及び
K個の型仮引数をもつ型を正確に一つ含む場合,≪単純名≫は,与えられた型実引数で構築され
た型を参照する。
− そうでない場合であって,≪using名前空間指令≫によって移入される名前空間が,名前I及び
180
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
K個の型仮引数をもつ型が二つ以上含む場合,≪単純名≫は,あいまいであり,コンパイル時エ
ラーになる。
注記 この完全の段階は,≪名前空間名又は型名≫(10.8参照)の処理内で対応する段階と
正確に類似している。
− それ以外の場合は,≪単純名≫は,未定義であり,コンパイル時エラーになる。
14.5.2.1 ブロック中の不変な意味
式中又は宣言中に出現する≪単純名≫として与えられたの識別子の各々について,すぐ外側の≪ブロッ
ク≫(15.2参照)又は≪switchブロック≫(15.7.2参照)の式中又は宣言中に現れる,同じ≪単純名≫
の他のすべての識別子は,同一実体を参照していなければならない。
注記 この規則は,ブロック内で名前の意味が常に同じであることを保証する。
例 次の例を考える。
class Test
{
double x;
void F(bool b) {
x = 1.0;
if (b) {
int x = 1;
}
}
}
xは,外側のブロック(これは,if文で入れ子になっているブロックを含む)中で異なる実体
を参照するので,コンパイル時エラーになる。
これとは対照的な例を示す。
class Test
{
double x;
void F(bool b) {
if (b) {
x = 1.0;
}
else {
int x = 1;
}
}
}
名前xは,外側のブロックでは使用されていないので,許される。
注記 不変な意味というこの規則は,単純名だけに適用される。同じ識別子が,単純名として一つの
意味をもち,メンバアクセス(14.5.4参照)の右辺演算対象として別の意味をもっていても問
題がない。
181
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例 次の例を考える。
struct Point
{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
この例は,インスタンス構築子の仮引数名としてフィールドの名前を使用するという,よく使
われる様式を示す。単純名x及びyは仮引数を参照しているが,メンバアクセス式this.x及び
this.yによるフィールドへのアクセスを禁止しない。
14.5.3 括弧付き式
≪括弧付き式≫は,括弧で囲まれた≪式≫からなる。
≪括弧付き式≫:
( ≪式≫ )
≪括弧付き式≫の評価は,括弧内の≪式≫の評価として行われる。括弧内の≪式≫が,名前空間又は,
型を表している場合は,コンパイル時エラーになる。それ以外の場合,≪括弧付き式≫の結果は,括弧内
の≪式≫を評価した結果となる。
14.5.4 メンバアクセス
≪メンバアクセス≫は,≪一次式≫,≪あらかじめ定義された型≫又は≪限定別名メンバ≫に続き,字
句“.”,≪識別子≫,及び省略可能な≪型実引数並び≫の順序で構成する。
≪メンバアクセス≫:
≪一次式≫ . ≪識別子≫ ≪型実引数並び≫opt
≪あらかじめ定義された型≫ . ≪識別子≫ ≪型実引数並び≫opt
≪限定別名メンバ≫ . ≪識別子≫ ≪型実引数並び≫opt
≪あらかじめ定義された型≫: 次のいずれか
bool
byte
char
decimal
double
float
int
long
object
sbyte
short
string
uint
ulong
ushort
≪限定別名メンバ≫は16.7で定義する。
Eが≪一次式≫,≪あらかじめ定義された型≫又は≪限定別名メンバ≫,Iが≪識別子≫,<A1, ..., Ak>
を省略可能な≪型実引数並び≫である,≪メンバアクセス≫は,形式E.Iの形式E.I<A1, ..., Ak>の
いずれかである。≪型実引数並び≫が指定されていない場合,kは0とみなす。≪メンバアクセス≫は,
次のとおりに評価分類される。
− kが0であって,Eが名前空間あり,Eが名前Iをもつ入れ子の名前空間を含む場合,その結果は,
その名前空間とする。
− そうでない場合であって,Eが名前空間であり,Eが名前I及びk個の型仮引数をもつアクセス可能
な型を含む場合,その結果は,指定された型実引数で構築される型とする。
182
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− Eが型に分類され,Eが型仮引数でなく,K個の型仮引数をもつ,EにおけるIのメンバ検索(14.3
参照)で一致があった場合は,E.Iは,次のとおりに評価分類される。
・ Iが型の場合,結果は,指定された型実引数及び推論された実引数で構築された型となる。
・ Iが一つ以上のメソッドを示す場合,結果は,インスタンス式が関連付けられていないメソッド群
となる。
・ Iがstatic特性の場合,結果は,インスタンス式に関連付けられていない特性アクセスとなる。
− Iがstaticフィールドの場合
・ フィールドがreadonlyで,フィールドが宣言されているクラス又は構造体の静的構築子の外側で
参照されている場合,結果は,値,すなわちEの中の静的フィールドIの値となる。
・ そうでなければ,結果は,変数,すなわちEの中の静的フィールドIとなる。
− Iがstaticイベントの場合
・ そのイベントが宣言されているクラス又は構造体の中で参照が行われていて,イベントが,≪イベ
ントアクセス子宣言群≫(17.7参照)を使用しないで宣言されている場合,E.Iは,Iが静的フィ
ールドであるかのように処理される。
・ それ以外の場合,結果は,インスタンス式が関連付けられていないイベントアクセスとなる。
− Iが定数の場合,結果は,値,すなわちその定数の値となる。
− Iが列挙のメンバの場合は,結果は,値,すなわちその列挙メンバの値となる。
− それ以外の場合は,E.Iは無効なメンバ参照であり,コンパイル時エラーになる。
− Eが特性アクセス,添字子アクセス,変数又は値であり,その型がTで,K個の型仮引数をもつTに
おけるIのメンバ検索(14.3参照)で一致があった場合は,E.Iは,次のとおりに評価分類される。
・ 最初に,Eが特性アクセス又は添字子アクセスならば,特性アクセス又は添字子アクセスの値が取
得(14.1.1参照)されて,Eは,値として再分類される。
・ Iが,一つ以上のメソッドを示す場合,結果は,Eのインスタンス式で関連付けられたメソッド群
となる。型仮引数が指定されている場合,そのメソッド群はK個の型仮引数をもつ総称メソッドだ
けで構成され,そのメソッド群内の個々のメソッドに対して暗黙に型実引数並びが適用される
(25.6.3参照)。型仮引数が指定されていない場合,そのメソッド群は総称メソッド及び総称でない
メソッドが含まれるかもしれない。
・ Iが,インスタンス特性を示す場合,結果は,Eのインスタンス式で関連付けられた特性アクセス
となる。
− Tが≪クラス型≫であり,Iがその≪クラス型≫のインスタンスフィールドを示す場合
・ Eの値がnullならば,System.NullReferenceExceptionが送出される。
・ それ以外の場合で,フィールドがreadonlyで,そのフィールドが宣言されているクラスのインス
タンス構築子の外側で参照が行われている場合,結果は,値,すなわちEによって参照されている
オブジェクト中のフィールドIの値となる。
・ そうでなければ,結果は変数,すなわちEによって参照されるオブジェクト中のフィールドIとな
る。
− Tが≪構造体型≫で,Iが,その≪構造体型≫のインスタンスフィールドを示す場合
・ Eが値の場合,又は,フィールドがreadonlyで,そのフィールドで宣言されている構造体のイン
スタンス構築子の外側で参照が行われている場合,結果は,値,すなわちEによって与えられる構
造体インスタンス中のフィールドIの値となる。
183
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
・ そうでなければ,結果は変数,すなわちEによって与えられる構造体インスタンス中のフィールド
Iとなる。
− Iがインスタンスイベントの場合
・ イベントが宣言されているクラス又は構造体の中で参照が行われて,イベントが≪イベントアクセ
ス子宣言群≫(17.7参照)なしに宣言される場合,E.Iは,Iがインスタンスフィールドであるか
のように処理される。
・ そうでなければ,結果は,Eのインスタンス式で関連付けられるイベントアクセスとなる。
− それ以外の場合,E.Iは無効なメンバ参照であり,コンパイル時エラーになる。
14.5.4.1 型名と同じ単純名
形式E.Iのメンバアクセスにおいて,Eが単一の識別子であり,≪単純名≫(14.5.2参照)としてのE
の意味が,≪型名≫(10.8参照)としてのEの意味と同じ型の定数,フィールド,特性,局所変数又は仮
引数となる場合,Eは,単純名及び型名の両方の意味を許される。Iは,いずれの場合においても,型E
のメンバでなければならないため,E.Iの意味があいまいとなることはない。すなわち,規則によって,
他の場合ならばコンパイル時エラーになる状況であったとしても,Eの静的メンバ及び入れ子型に対する
アクセスが許される。
例
struct Color
{
public static readonly Color White = new Color(…);
public static readonly Color Black = new Color(…);
public Color Complement() {…}
}
class A
{
public Color Color;
// Field A.Color of type Color
void F() {
Color = Color.Black;
// References Color.Black
Color = Color.Complement(); // Invokes Complement() on
A.Color
}
static void G() {
Color c = Color.White;
// References Color.White
}
}
クラスAの中では,型Colorを指す識別子Colorには,下線を付けた。フィールドColor
を指す識別子には,下線を施していない。
14.5.5 呼出し式
呼出し式は,メソッドを呼び出すために使われる。
≪呼出し式≫:
≪一次式≫ ( ≪実引数並び≫opt )
184
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪呼出し式≫の≪一次式≫は,メソッド群又は≪委譲型≫の値でなければならない。≪一次式≫がメソ
ッド群の場合,≪呼出し式≫は,メソッド呼出しとなる(14.5.5.1参照)。≪一次式≫が≪委譲型≫の値の
場合,≪呼出し式≫は,委譲呼出し(14.5.5.2参照)となる。≪一次式≫がメソッド群でも≪委譲型≫の
値でもない場合は,コンパイル時エラーになる。
省略可能な≪実引数並び≫(14.4.1参照)は,メソッドの仮引数に対して値又は変数参照を与える。
≪呼出し式≫の評価結果は,次に分類される。
− ≪呼出し式≫が呼び出すメソッド又は委譲がvoidを返す場合,結果は“なし”とする。“なし”に分
類される式は,どんな演算子の演算対象にもならず,≪式文≫(15.6参照)の文脈においてだけ許可
される。
− それ以外の場合,結果は,メソッド又は委譲で返される型の値となる。
14.5.5.1 メソッド呼出し
メソッド呼出しの場合,≪呼出し式≫の≪一次式≫はメソッド群でなければならない。メソッド群は,
呼び出すメソッド,又は実際に呼び出すメソッドの候補になる多重定義されたメソッド集合を示す。後者
の場合,実際呼び出すメソッドの決定は,≪実引数並び≫の実引数の型で指定される文脈に基づく。
メソッド群M(≪型実引数並び≫を含むものもある),省略可能な≪実引数並び≫Aにおいて,形式M(A)
のメソッド呼出しのコンパイル時の処理は,次の手順からなる。
− メソッド呼出しに対する候補メソッド集合を構築する。メソッド群Mに関連する個々のメソッドFに
おいて,
・ Fが総称でなければ,次の場合にFが候補になる。
− Mが型実引数並びをもたない。及び,
− FがAに関して適応可能である(14.4.2.1参照)。
・ Fが総称であって,Mが型実引数並びをもたない場合,次の場合にFが候補になる。
− 型推論(25.6.4参照)が成功し,呼出しの際の型実引数の並びが推論されるとき。及び,
− 推論された型が,対応するメソッドの型仮引数に置換されると,Fの仮引数並び内のすべての構
築型が,それらの制約(25.7.1参照)を満足し,Fの仮引数並びはAに関して適用可能である場
合。
・ Fが総称であって,Mが型実引数並びを含む場合,次の場合にFが候補になる。
− Fが型実引数並びを与えられたのと同じメソッド型仮引数の個数をもつ場合。及び,
− 推論された型が,対応するメソッドの型仮引数に置換されると,Fの仮引数並び内のすべての構
築型が,それらの制約(25.7.1参照)を満足し,Fの仮引数並びはAに関して適用可能である場
合。
− 候補メソッドの集合が,最派生型からメソッドだけを含むように削減する。CがメソッドFを宣言し
ている型としたとき,その集合内の個々のメソッドC.Fに対して,Cの基底クラスで宣言されたすべ
てのメソッドは,その集合から取り除かれる。さらに,Cがobujectでないクラスの場合,インタ
フェース型で宣言されたすべてのメソッドが,その集合から取り除かれる。
注記 この後半の規則は,メソッド群が,object以外の実効基底クラス及び空でない実効イン
タフェース集合をもつ型仮引数におけるメンバ検索の結果となるときだけに効果がある。
− 候補メソッドの結果集合が空ならば,適用可能なメソッドが存在しないため,コンパイル時エラーに
なる。
− 多重定義解決(14.4.2参照)規則を用いて候補メソッド集合から最良メソッドを選びだす。単一の最
185
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
良メソッドが選べなかった場合,メソッドの呼出しはあいまいであり,コンパイル時エラーになる。
多重定義解決が行われているとき,対応するメソッドの型仮引数を(指定されたか,又は推論された)
型実引数に置換した後に,総称メソッドの仮引数が考慮される。
− 選択された最良メソッドの最終的な妥当性を,次のように検証する。
・ そのメソッドがメソッド群の文脈で検証される。最良メソッドが静的メソッドならば,メソッド群
は,型を通じて≪単純名≫又は≪メンバアクセス≫から得られたものでなければならない。最適な
メソッドがインスタンスメソッドならば,メソッド群は,≪単純名≫から,変数若しくは値を通じ
て≪メンバアクセス≫から得られたもの,又は≪baseアクセス≫から得られたものでなければな
らない。いずれの要件も満たさない場合,コンパイル時エラーになる。
・ 最適なメソッドが総称メソッドの場合,型実引数が,その総称メソッドで宣言している制約(25.7.1
参照)に対して検証する。任意の型実引数が,対応する型仮引数の制約を満足しない場合,コンパ
イル時エラーになる。
メソッドが,これらの手順によってコンパイル時に選択検証されたら,実際の実行時呼出しは,14.4.3
で規定した関数メンバ呼出し規則に従って処理される。
注記 上で説明した解決規則の直感的結果は,次のようなものになる。メソッド呼出しで呼び出され
るメソッドを探し出すには,メソッド呼出しで示されている型から始めて,適用可能で,アク
セス可能で,上書きではないメソッドの宣言が少なくとも一つ見つかるまで,継承連鎖を上に
たどる。次に,その型で宣言される適用可能で,アクセス可能で,上書きではないメソッドの
集合に対して,多重定義解決を実施して,その結果選択されたメソッドを呼び出す。
14.5.5.2 委譲の呼出し
委譲の呼出しでは,≪呼出し式≫の≪一次式≫に≪委譲型≫の値を指定しなければならない。さらに,
≪委譲型≫が,≪委譲型≫と同じ仮引数並びをもつ関数メンバであることを考えると,≪委譲型≫は,≪
呼出し式≫の≪実引数並び≫に対して適用可能(14.4.2.1参照)でなければならない。
Dが≪委譲型≫の≪一次式≫で,Aが省略可能な≪実引数並び≫である形式D(A)の委譲呼出しの実行時
処理は,次の手順で行われる。
− Dを評価する。この評価で例外が発生すると,これ以降の手順は実行されない。
− 引数並びAを評価する。この評価で例外が発生すると,これ以降の手順は実行されない。
− Dの値が有効であるか検査する。Dの値がnullならば,System.NullReferenceExceptionが送
出され,これ以降の手順は実行されない。
− そうでなければ,Dは委譲インスタンスの参照である。委譲によって参照されるメソッドに対して関
数メンバ呼出し(14.4.3参照)が実行される。呼出し可能実体の呼出しで例外が発生すると,残りの
呼出し可能実体の呼出しは行われない。そうでなければ,その呼出しの結果は,最後の呼出し可能実
体からの結果とする。
14.5.6 要素アクセス
≪要素アクセス≫は,≪一次非配列生成式≫,字句“[”,≪式並び≫,字句“]”の順序で構成される。
≪式並び≫は,コンマで区切られた一つ以上の式で構成される。
≪要素アクセス≫:
≪一次非配列生成式≫ [ ≪式並び≫ ]
≪式並び≫:
≪式≫
186
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪式並び≫ , ≪式≫
≪要素アクセス≫の≪一次非配列生成式≫が,≪配列型≫の値ならば,≪要素アクセス≫は,配列アク
セス(14.5.6.1参照)となる。それ以外の場合,≪一次非配列生成式≫は,一つ以上の添字子メンバをも
つクラス型,構造体型又はインタフェース型の変数又は値でなければならない。この場合の≪要素アクセ
ス≫は添字子アクセス(14.5.6.2参照)となる。
14.5.6.1 配列アクセス
配列アクセスでは,≪要素アクセス≫の≪一次非配列生成式≫は,≪配列型≫の値でなければならない。
≪式並び≫中の式の個数は,≪配列型≫の位階と同じでなければならない。さらに,各式は,型int,uint,
long,ulong又はこれらの型の一つ以上に暗黙に変換できる型でなければならない。
配列アクセスを評価した結果は,配列の要素型の変数,すなわち≪式並び≫の式の値によって選択され
た配列要素となる。
Pが≪配列型≫の≪一次非配列生成式≫で,Aが≪式並び≫である形式P[A]の配列アクセスの実行時処
理は,次の手順で行われる。
− Pが評価される。この評価で例外が発生すると,これ以降の手順は実行されない。
− ≪式並び≫の添字式を左から右に評価する。各添字式の評価に続いて,int,uint,long又はulong
のいずれかの型への暗黙の変換(13.1参照)を行う。この並びの中で暗黙の変換が存在する最初の型
が選択される。例えば,添字式の型が,shortの場合,shortからint及びshortからlongへの
二つの暗黙の変換が可能だが,intへの暗黙の変換が実行される。添字式の評価,又はそれに続く暗
黙の変換で例外が発生した場合は,以降の添字式は評価されず,これ以降の手順は実行されない。
− Pの値が有効か検査する。Pの値がnullの場合,System.NullReferenceExceptionが送出され,
これ以降の手順は実行されない。
− ≪式並び≫の各式の値を,Pで参照される配列インスタンスの各次元の実際の範囲と照合する。範囲
外の値が存在した場合,system.IndexOutOfRangeExceptionが送出され,これ以降の手順は実
行されない。
− 添字式で指定された配列要素の位置を計算し,この位置を配列アクセスの結果とする。
14.5.6.2 添字子アクセス
添字子アクセスの場合,≪要素アクセス≫の≪一次非配列生成式≫は,クラス型,構造体型又はインタ
フェース型の変数又は値でなければならない。さらに,この型は,≪要素アクセス≫の≪式並び≫に関し
て適用可能な添字子を一つ以上実装していなければならない。
型Tのクラス,構造体又はインタフェースの≪一次非配列生成式≫P,≪式並び≫Aにおける形式P[A]
の添字子アクセスのコンパイル時処理は,次の手順からなる。
− Tによって提供される添字子の集合を構築する。この集合は,T又はTの基底型で宣言されていて,
override宣言がなく,現在の文脈でアクセス可能なすべての構築子で構成される。
− 集合を絞り込んで,他の添字子によって隠ぺいされていない適用可能な添字子だけにする。添字子I
が宣言されている型をSとして,集合内の各添字子S.Iに対して次の規則を適用する。
・ IがAに対して適用可能でない(14.4.2.1参照)場合,Iを集合から削除する。
・ IがAに対して適用可能である(14.4.2.1参照)場合,Sの基底型で宣言されるすべての添字子を集
合から削除する。
− 候補添字子の結果集合が空の場合は,適用可能な添字子が存在しないため,コンパイル時エラーにな
る。
187
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 多重定義解決規則(14.4.2参照)を用いて,候補添字子集合の最良の添字子を識別する。単一の最良
添字子が識別されなかった場合は,添字子アクセスはあいまいであり,コンパイル時エラーになる。
− ≪式並び≫の添字式を左から右に評価する。添字子アクセスの処理結果は,添字子アクセスとして分
類される式になる。添字子アクセス式は,上記の手順で決定された添字子を参照し,インスタンス式
P及び実引数並びAに関連付けられる。
使用される文脈に基づいて,添字子アクセスによって添字子の≪getアクセス子≫又は≪setアクセス
子≫が呼び出される。添字子アクセスが,代入の対象である場合,≪setアクセス子≫が,新しい値(14.14.1
参照)を代入するために呼び出される。それ以外の場合,≪getアクセス子≫が,現在の値(14.1.1参照)
を取得するために呼び出される。
14.5.7 thisアクセス
≪thisアクセス≫は,予約語thisで構成される。
≪thisアクセス≫:
this
≪thisアクセス≫を使用できるのは,インスタンス構築子,インスタンスメソッド又はインスタンス
アクセス子の≪ブロック≫の中だけとする。≪thisアクセス≫の意味は,次のいずれかである。
− クラスのインスタンス構築子の≪一次式≫で使われる場合,thisは値として分類される。値の型は
thisが使用されているクラスであり,値は構築されているオブジェクトへの参照となる。
− クラスのインスタンスメソッド又はインスタンスアクセス子の≪一次式≫で使われる場合,thisは
値として分類される。値の型は,thisが使用されているクラスであり,値は,メソッド又はアクセ
ス子の呼出しの対象になっているオブジェクトへの参照となる。
− 構造体のインスタンス構築子の≪一次式≫で使われる場合,thisは変数として分類される。変数の
型はthisが使用されている構造体であり,その変数は構築されている構造体を表現する。構造体の
インスタンス構築子のthis変数は,次のように振る舞う。
・ その構築子宣言に構築子初期化子をもたない場合,その構造体型のout仮引数と全く同様に振る舞
う。すなわち,この変数はインスタンス構築子のすべての実行経路で確実に代入されていなければ
ならないことを意味する。
・ その構築子宣言に構築子初期化子をもつ場合,その構造体型のref仮引数と全く同様に振る舞う。
具体的には,この変数は初期代入されていることを意味する。
− 構造体のインスタンスメソッド又はインスタンスアクセス子の≪一次式≫で使われる場合,thisは
変数として分類される。変数の型は,thisが使用されている構造体であり,変数は,メソッド又は
アクセス子の呼出しの対象になっている構造体を表す。構造体インスタンスメソッドのthis変数は,
構造体型のref仮引数と全く同様に振る舞う。
上記以外の文脈の≪一次式≫でthisを使用すると,コンパイル時エラーになる。具体的には,静的メ
ソッド,静的特性アクセス子又はフィールド宣言の≪変数初期化子≫では,thisを参照できない。
14.5.8 baseアクセス
≪baseアクセス≫は,予約語base,その後に,字句“.”及び識別子又は,省略可能な≪型実引数並
び≫若しくは角括弧に囲まれた≪式並び≫のいずれかで構成される。
≪baseアクセス≫:
base . ≪識別子≫ ≪型実引数並び≫opt
base [ ≪式並び≫ ]
188
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪baseアクセス≫は,現在のクラス又は構造体の同じ名前のメンバによって隠ぺいされている,基底
クラスのメンバにアクセスするために使用する。≪baseアクセス≫を使用できるのは,インスタンス構
築子,インスタンスメソッド又はインスタンスアクセス子の≪ブロック≫の中に限られる。base.Iが,
クラス又は構造体で現れるとき,Iは,そのクラス又は構造体の基底クラスのメンバを表さなければなら
ない。同様に,base[E]がクラスで現れるとき,適用できる添字子が,基底クラスに存在しなければなら
ない。
コンパイル時に,形式base.I及び形式base[E]の≪baseアクセス≫式は,構築が行われるクラス又
は構造体の基底クラスをBとして,あたかも((B)this).I及び((B)this)[E]であるかのように評価さ
れる。したがって,thisが基底クラスのインスタンスであることを除いては,base.I及びbase[E]は,
this.I及びthis[E]に対応する。
≪baseアクセス≫が,仮想関数メンバ(メソッド,イベント,特性又は添字子)を参照するとき,実
行時において呼び出す関数メンバ決定(14.4.3参照)が変更される。baseアクセスでない場合,通常this
の実行時型を用いるが,その代わりにBを用いて,関数メンバの最派生実装(17.5.3参照)を探し出して,
呼び出す関数メンバを決定する。したがって,virtual関数メンバのoverride(上書き)の内部では,
≪baseアクセス≫が,関数メンバの継承実装を呼び出すことができる。≪baseアクセス≫で参照される
関数メンバが抽象メンバならば,コンパイル時エラーになる。
14.5.9 後置増加演算子及び後置減少演算子
≪後置増加式≫:
≪一次式≫ ++
≪後置減少式≫:
≪一次式≫ --
後置増加演算子又は後置減少演算子の演算対象は,変数,特性アクセス又は添字子アクセスに分類され
る式でなければならない。演算結果は,演算対象と同じ型の値になる。
後置増加演算又は後置減少演算の演算対象が,特性アクセス又は添字子アクセスの場合,その特性又は
添字子には,get及びsetの両アクセス子がなければならない。そうでないと,コンパイル時エラーにな
る。
演算子実装選択には,単項演算子の多重定義解決(14.2.3参照)が適用される。型sbyte,byte,short,
ushort,int,uint,long,ulong,char,float,double,decimal及びすべての列挙型につい
ては,あらかじめ定義された演算子++及びあらかじめ定義された演算子--が存在する。あらかじめ定義さ
れた演算子++は,演算対象に1を加えた値を返し,あらかじめ定義された演算子--は,演算対象から1
を減算した値を返す。check文脈では,この加算又は減算の結果が返却型の範囲を超える場合であって,
返却型が整数型又は列挙型の場合,System.OverflowExceptionが送出される。
選択された単項演算子の返却型から≪一次式≫の型への暗黙の変換が存在していなければならず,そう
でなければコンパイル時にエラーとする。
形式x++又は形式x--の後置増加演算又は後置減少演算の実行時処理は,次の手順からなる。
− xが変数に分類される場合
・ xを評価して変数を生成する。
・ xの値を保存する。
・ 保存したxの値を選択した演算子を演算対象の型に変換し,その値を実引数として選択した演算子
を呼び出す。
189
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
・ 演算子からの返却値をxの型に変換し,xの評価によって得られた場所に格納する。
・ 保存したxの値が,その演算の結果となる。
− xが特性アクセス又は添字子アクセスに分類される場合
・ xに関連付けられた,インスタンス式(xがstaticでない場合)及び実引数並び(xが添字子ア
クセスの場合)を評価して,その結果を,その後のgetアクセス子及びsetのアクセス子呼出し
に使う。
・ xのgetアクセス子が呼び出され,返却値を保存する(以降,保存したxと表現する)。
・ 保存したxの値を選択した演算子の演算対象の型に変換し,その値を実引数として,選択した演算
子が呼び出される。
・ 演算子からの返却値をxの型に変換し,その値をvalue引数として,xのsetアクセス子を呼び
出す。
・ 保存したxの値が,その演算の結果となる。
演算子++及び演算子--には,前置記法(14.6.5参照)も使用できる。x++又はx--の結果が演算の前の
xの値であるのに対して,++x又は--xの結果は,演算の後のxの値となる。いずれの場合も,x自体の
値は,演算を行った後の値となる。
operator ++又はoperator --の実装は,後置記法でも前置記法でも呼び出すことができる。この二
つの記法に対して,演算子を別個に実装することはできない。
上で定義したあらかじめ定義された非もち上げの後置増加演算子又は後置減少演算子のもち上げ形式
(14.2.7参照)も,あらかじめ定義されている。
14.5.10
new演算子
new演算子は,その型の新しいインスタンスを生成するために使用される。
new式には,次の三つの形式がある。
− クラス型及び値型の新しいインスタンスを生成するには,オブジェクト生成式を使用する。
− 配列型の新しいインスタンスを生成するには,配列生成式を使用する。
− 委譲型の新しいインスタンスを生成するには,委譲生成式を使用する。
new演算子は,型のインスタンス生成だけを意味して,メモリの動的割当てを必ずしも意味しない。特
に,値型のインスタンスでは,newが値型のインスタンスを生成するために使われても,今の変数以外の
追加メモリを必要としないし,動的割当ても発生しない。
14.5.10.1
オブジェクト生成式
≪オブジェクト生成式≫は,≪クラス型≫又は≪値型≫の新しいインスタンスを生成するために使用さ
れる。
≪オブジェクト生成式≫:
new ≪型≫ ( ≪実引数並び≫opt )
≪オブジェクト生成式≫の≪型≫は,≪クラス型≫,≪値型≫又は≪構築子制約≫若しくは値型制約
(25.7参照)をもつ≪型仮引数≫でなければならない。その≪型≫は,abstractな≪クラス型≫とする
ことはできない。
省略可能な≪実引数並び≫(14.4.1参照)は,≪型≫が≪クラス型≫又は≪構造体型≫の場合だけ許さ
れ,≪型仮引数≫は許可されない。
≪クラス型≫,≪値型≫又は≪型仮引数≫のT,省略可能な≪実引数並び≫ Aにおける,形式new T(A)
の≪オブジェクト生成式≫のコンパイル時処理は,次の手順からなる。
190
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− Tが≪値型≫であり,Aが指定されていない場合
・ ≪オブジェクト生成式≫は省略時構築子呼出しとなる。≪オブジェクト生成式≫の結果は,型Tの
値,すなわち11.1.2で定義されるTの省略時の値となる。
− そうでない場合であって,Tが≪クラス型≫又は≪構造体型≫の場合
・ Tがabstractな ≪クラス型≫ならば,コンパイル時エラーになる。
・ 多重定義解決規則(14.4.2参照)を用いて,呼び出すインスタンス構築子を決定する。インスタン
ス構築子の候補集合は,Tで宣言されているすべてのアクセス可能なインスタンス構築子となる。
インスタンス構築の候補集合が空,又は単一の最良インスタンス構築子を識別できないならば,コ
ンパイル時エラーになる。
・ ≪オブジェクト生成式≫の結果は,型Tの値,すなわち上の段階で決定されたインスタンス構築子
を呼び出して生成された値となる。
− それ以外の場合であって,Tが≪型仮引数≫の場合
・ Aが指定されいる場合,コンパイル時エラーになる。
・ そうでない場合であって,Tが≪構築子制約≫若しくは値型制約をもつ場合,≪オブジェクト生成
式≫の結果は,型Tの値となる。
・ そうでなければ,≪オブジェクト生成式≫は無効であり,コンパイル時エラーになる。
− それ以外の場合,≪オブジェクト生成式≫は無効であり,コンパイル時エラーになる。
≪クラス型≫,≪構造体型≫又は≪型仮引数≫のT,省略可能な≪実引数並び≫ Aにおける,形式new
T(A)の≪オブジェクト生成式≫の実行時処理は,次の手順からなる。
− Tが≪クラス型≫の場合
・ クラスTの新しいインスタンスが割り当てられる。新しいインスタンスに割り当てる十分なメモリ
がない場合,System.OutOfMemoryExceptionが送出され,以降の手順は実行されない。
・ 新しいインスタンスのすべてのフィールドが,省略時の値(12.2参照)に初期化される。
・ インスタンス構築子が,関数メンバ呼出し(14.4.3参照)の規則に従って呼び出される。新しく割
り当てたインスタンスへの参照が,インスタンス構築子に自動的に渡され,その構築子の中からは
thisでインスタンスにアクセスできる。
− Tが≪構造体型≫の場合
・ 一時的な局所変数を割り当てて,型Tのインスタンスを生成する。≪構造体型≫のインスタンス構
築子は,生成しているインスタンスの各フィールドに値を確実に代入しなければならないため,一
時変数の初期化は必要としない。
・ 関数メンバ呼出しの規則(14.4.3参照)に従って,インスタンス構築子が呼び出される。新しく割
り当てたインスタンスへの参照が,インスタンス構築子に自動的に渡され,その構築子の中からは
thisでインスタンスにアクセスできる。
− Tが≪型仮引数≫の場合
・ 実行時にTが値型に評価される場合,結果は,この型省略時の値とする(11.1.2参照)。Tが値型制
約をもつ場合は,常にそうなる。
・ そうでなければ,実行時にTが,引数をとらない公開構築子をもつ非抽象クラス型に評価される。
結果は,このクラス型の新しいインスタンスとする。これは,次の手順からなる。
− そのクラスの新しいインスタンスが割り当てられる。新しいインスタンスに割り当てる十分なメ
モリがない場合,System.OutOfMemoryExceptionが送出され,以降の手順は実行されない。
191
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 新しいインスタンスのすべてのフィールドが,省略時の値(12.2参照)に初期化される。
− インスタンス構築子が,関数メンバ呼出し(14.4.3参照)の規則に従って呼び出される。新しく
割り当てたインスタンスへの参照が,インスタンス構築子に自動的に渡され,その構築子の中か
らはthisでインスタンスにアクセスできる。
注記 実行時型は,≪オブジェクト生成式≫を含んでいるコードにアクセス可能である必要はない。
それにもかかわらず,実行時環境は,その構築子呼出しを可能にしなければならない。
14.5.10.2
配列生成式
≪配列生成式≫は,≪配列型≫の新しいインスタンスを生成するために使用される。
≪配列生成式≫:
new ≪非配列型≫ [ ≪式並び≫ ] ≪位階指定子群≫opt ≪配列初期化子≫opt
new ≪配列型≫ ≪配列初期化子≫
配列生成式の第1の形式は,式並びから個別の式を削除した結果得られる形式の型で配列インスタンス
を割り当てる。
例 配列生成式new int[10,20]は,型int[,]の配列インスタンスを作り出し,配列生成式new
int[10][,]は,型int[][,]の配列を作り出す。
式並びの各式は,型int,uint,long若しくはulongであるか,又はこれらの型の一つ以上に暗黙
に変換される型でなければならない。各式の値が,新しく割り当てられた配列インスタンスの対応する次
元の長さを決定する。配列次元の長さは負の値にできないため,負の値をもつ定数式が式並びの中にある
とコンパイル時エラーとなる。
安全でない文脈(25.1参照)の場合を除き,配列の配置は未規定とする。
第1の形式の配列生成式で配列初期化子を指定する場合は,式並びの各式には定数を指定し,式並びで
指定する位階及び次元の長さが,配列初期化子のそれらと一致していなければならない。
第2の形式の配列生成式では,指定された配列型の位階が,配列初期化子と一致していなければならな
い。個別の次元の長さは,配列初期化子の対応する各入れ子水準での要素の個数から推論される。次に例
を示す。
new int[,] {{0, 1}, {2, 3}, {4, 5}}
上の式は,下の式と正確に対応する。
new int[3, 2] {{0, 1}, {2, 3}, {4, 5}}
配列初期化子は,19.7で規定する。
配列生成式を評価した結果は,値として分類される。すなわち,新しく割り当てられた配列インスタン
スへの参照となる。配列生成式の実行時の処理は,次の手順からなる。
− ≪式並び≫における次元の長さの各式が,左から右に順に評価される。各式の評価に続いて,int,
uint,long,ulongのいずれかの型への暗黙の変換(13.1参照)が行われる。この並びの中で暗黙
の変換が存在する最初の型が選択される。式の評価又はそれに続く暗黙の変換で例外が発生した場合
は,以降の式は評価されず,これ以降の手順は実行されない。
− 次元の長さのために計算された値が,検証される。一つ以上の値が,ゼロより小さい場合,
System.OverflowExceptionが送出され,以降の手順は実行されない。
− 指定された次元の長さの配列インスタンスを割り当てる。新しいインスタンスを割り当てるのに十分
なメモリが存在しない場合,System.OutOfMemoryExceptionが送出され,以降の手順は実行され
ない。
192
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 新しい配列インスタンスのすべての要素が,省略時の値(12.2参照)に初期化される。
− 配列生成式に配列初期化子が含まれている場合は,配列初期化子の各式を評価し,対応する配列要素
に代入する。評価及び代入を,配列初期化子で式が記述されている順序で実行する。すなわち,要素
の初期化は,添字の昇順に,右端の次元の添字を最初に増加させて行う。指定された式の評価又は対
応する配列要素への代入で例外が発生した場合は,それ以降の要素は初期化しない。したがって,残
りの要素は省略時の値のままとなる。
配列生成式では,配列型の要素をもつ配列も具現化できるが,このような配列の要素そのものは,手動
で初期化しなければならない。
例 次に例を示す。
int[][] a = new int[100][];
これは,型int[]の要素を100個もつ一次元配列を生成する。各要素の初期値は,nullであ
る。この配列生成式で(配列初期化子を指定せずに)部分配列の初期化を行うことは,不可能で
ある。
int[][] a = new int[100][5];
// Error
上の文はコンパイル時エラーになる。部分配列の具現化は,次のように手動で行うか,配列初
期化子を使わなければならない。
int[][] a = new int[100][];
for (int i = 0; i < 100; i++) a[i] = new int[5];
int[][] b = new int[3][] { new int[1], new int[2], new int[3] };
注記 配列の配列が“四角形”の場合,すなわち部分配列がすべて同じ長さの場合は,多次元配列に
するほうが効率的となる。上の例では,配列の配列を具現化すると,101個のオブジェクト(外
側の配列が1個及び100個の部分配列)が生成される。次に示すのは,対照的な多次元配列の
例である。
int[,] = new int[100, 5];
この例では,2次元配列のオブジェクトが一つ生成されるだけであり,一つの文で割当てを
行うことができる。
14.5.10.3
委譲生成式
≪委譲生成式≫は,≪委譲型≫の新しいインスタンスを生成するために使用される。
≪委譲生成式≫:
new ≪委譲型≫ ( ≪式≫ )
委譲生成式の引数は,メソッド群(14.1参照),≪無名メソッド式≫(14.5.15参照)又は≪委譲型≫の
値でなければならない。実引数がメソッド群ならば,メソッド及び型仮引数が定まり,それがインスタン
スメソッドの場合は委譲を生成する対象のオブジェクトも定まる。実引数が≪委譲型≫の値ならば,コピ
ーを生成する委譲インスタンスが定まる。
≪式≫が≪無名メソッド式≫(14.5.15参照)の場合,無名メソッドは13.5で規定した暗黙変換規則を使
って,指定された委譲型に変換される。
例 次に例を示す。Dが委譲型のとき
new D(delegate { Console.WriteLine("Hello"); })
これは,次の式と等価である。
(D) delegate { Console.WriteLine("Hello"); }
193
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪委譲型≫をD,≪式≫を Eとしたとき,形式new D(E)の≪委譲生成式≫のコンパイル時処理は,次
の手順からなる。
− Eがメソッド群の場合
・ 次に示す変更を行って,形式E(A)のメソッド呼出しに対応している単一のメソッドを選択する。
− Dの型仮引数及び修飾子(ref又はout)が,実引数並びAの実引数の型及び修飾子として使わ
れる。
− 考慮された候補メソッドは,通常形式(14.4.2.1参照)の適用可能なメソッドだけとし,展開形式
だけでの適用可能なメソッドではない。
− 14.5.5.1のアルゴリズムでエラーになる場合,コンパイル時エラーになる。そうでなければ,その
アルゴリズムで単一で最良の,Dと同じ数の仮引数をもち,(メソッドが総称の場合)その型実引
数とともにメソッドMが生成される。
・ 選択されたMが,委譲型Dと一貫性がなければ(22.1参照),コンパイル時エラーとなる。
・ 選択されたMがインスタンスメソッドであれば,Eに関連するインスタンス式は,委譲の対象オブ
ジェクトを決定する。
・ 結果は,D型の値,すなわち,選択されたメソッド及び対象のオブジェクトを示す,新しく生成さ
れた委譲となる。
− そうでない場合であって,Eが≪委譲型≫の値の場合
・ D及びEの仮引数の数が異なる場合,コンパイル時エラーになる。
・ Eの返却型からDの返却型への恒等変換又は暗黙の参照変換がなければ,コンパイル時エラーにな
る。
・ Dのある仮引数に対して,その仮引数の型からEの対応する仮引数の型への恒等変換又は暗黙の参
照変換がなければ,コンパイル時エラーになる。
・ D又はEの任意の仮引数が,仮引数修飾子(ref又はout)をもつならば,他の対応する仮引数も,
同じ修飾子及び同じ型をもたなければならない。
・ 結果は,型Dの値,すなわち,Eと同じ呼出し並びを参照する新しく生成された委譲となる。
− そうでなければ,委譲生成式は無効であり,コンパイル時エラーになる。
≪委譲型≫をDとし,≪式≫をEとしたとき,形式new D(E)の≪委譲生成式≫の実行時処理は,次の
手順からなる。
− Eがメソッド群の場合
・ コンパイル時に選択されたメソッドが静的メソッドならば,委譲の対象オブジェクトはnullとす
る。そうでなければ,選択されたメソッドはインスタンスメソッドであり,Eに関連付けられたイ
ンスタンス式によって,委譲の対象オブジェクトが決定される。
− インスタンス式を評価する。この評価で例外が発生すると,以降の手順は実行されない。
− インスタンス式が≪参照型≫ならば,インスタンス式によって計算された値が対象オブジェクト
になる。対象オブジェクトがnullならば,System.NullReferenceExceptionが送出され,
以降の手順は実行されない。
− インスタンス式が≪値型≫ならば,ボックス化演算(11.3.1参照)を行って,値をオブジェクト
に変換し,このオブジェクトを対象オブジェクトとする。
・ 委譲型Dの新しいインスタンスが割り当てられる。新しいインスタンスを割り当てるための十分な
メモリが存在しない場合,System.OutOfMemoryExceptionが送出され,以降の手順は実行され
194
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ない。
・ 新しい委譲インスタンスが,コンパイル時に決定されたメソッドに対する参照及び上の段階で計算
された対象オブジェクトに対する参照を使って,初期化される。
− Eが≪委譲型≫の値の場合
・ Eが評価される。この評価で例外が発生すると,それ以上の手順は実行されない。
・ Eの値がnullの場合,System.NullReferenceExceptionが送出され,以降の手順は実行され
ない。
・ 委譲型Dの新しいインスタンスが割り当てられる。新しいインスタンスを割り当てるための十分な
メモリが存在しない場合,System.OutOfMemoryExceptionが送出され,以降の手順は実行され
ない。
・ 新しい委譲インスタンスが,Eで与えられる委譲インスタンスと同じ呼出し並びへの参照で初期化
される。
委譲が参照するメソッド及びオブジェクトは,委譲が具現化されるときに決定され,委譲の有効期間を
通じて一定とする。すなわち,生成された委譲の対象メソッド及びオブジェクトは,変更できない。
注記 二つの委譲が結合されるか,一つの委譲が別の委譲から削除されるときには,結果が新しい委
譲となることに注意する。既存の委譲の内容が変更されることはない。
特性,添字子,利用者定義演算子,インスタンス構築子,終了化子又は静的構築子に対する委譲は生成
できない。
例 既に述べたように,メソッド群から委譲を生成するとき,委譲の仮引数並び及び返却値の型が,
多重定義されたメソッドの中から選択されるものを決める。次に例を示す。
delegate double DoubleFunc(double x);
class A
{
DoubleFunc f = new DoubleFunc(Square);
static float Square(float x) {
return x * x;
}
static double Square(double x) {
return x * x;
}
}
フィールドA.fは,2番目のメソッドSquareを参照する委譲で初期化される。それは,double
値単独で構成される実引数並びによる多重定義解決によって,そのメソッドが選択されるからで
ある。もし,2番目のSquareメソッドが存在していない場合は,コンパイル時エラーになる。
委譲型(22.1参照)を一貫性のあるメソッドの定義は,返却型に対して共変にでき,仮引数型
に対して反変にできる。つまり,メソッドの返却型は,委譲の返却型より特殊性を高くでき,メ
ソッドの仮引数型は,委譲の仮引数型より特殊性を低くできる。
delegate double StrToObj(string s);
class A
{
195
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
StrToObj f = new StrToObj(ObjToStr);
static string ObjToStr(object x) {
return x.ToString();
}
}
メソッドObjToStrは,型StrToObjの委譲を生成するために使用される。このメソッドは,
その委譲と一貫性がある。なぜなら,委譲の仮引数型からメソッドの仮引数型への暗黙の参照変
換及び,メソッドの返却型から委譲の返却型への暗黙の参照変換が存在するからである。
次に,別の例を示す。
delegate void D1(int i);
delegate void D2(long l);
class program
{
static void M(long l) { }
static void F(D1 d) { }
static void F(D2 d) { }
static void Main() {
D1 d1 = new D1(M);
// Error: M is compatible with D1
// but inconsistent, because the conversion from
// int to long is not an implicit reference
// conversion.
F(M); // Error: ambiguity between F(D1) and F(D2). M is
// compatible with both D1 and D2. However M is
// consitent with D2, while inconsistent with D1.
}
}
14.5.11
typeof演算子
typeof演算子は,型に対するSystem.Typeオブジェクトを取得するために使用される。
≪typeof式≫:
typeof ( ≪型≫ )
typeof ( ≪非束縛型名≫ )
typeof ( void )
≪非束縛型名≫:
≪識別子≫ ≪総称次元指定子≫opt
≪識別子≫ :: ≪識別子≫ ≪総称次元指定子≫opt
≪非束縛型名≫ . ≪識別子≫ ≪総称次元指定子≫opt
≪総称次元指定子≫:
< ≪コンマ群≫opt >
196
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪コンマ群≫:
,
≪コンマ群≫ ,
第1の形式の≪typeof式≫は,キーワードtypeof及び括弧で囲んだ≪型≫となる。この形式の式の
結果は,指定した型に対するSystem.Typeオブジェクトとなる。どの型に対してもSystem.Typeオブ
ジェクトは一つだけとなる。
注記 これは,型Tについて,typeof(T) == typeof(T)が常に真であることを意味する。
第2の形式の≪typeof式≫は,キーワードtypeof及び括弧で囲んだ≪非束縛型名≫となる。
注記 ≪非束縛型名≫は,≪型名≫に非常に似ているが,≪非束縛型名≫には,≪型名≫に≪型実引
数並び≫を含む≪総称次元指定子≫が含まれる。
≪typeof式≫の演算対象が≪非束縛型名≫及び≪型名≫の両方の文法を満足する一連の字句である場
合,すなわち,≪typeof式≫の演算対象に≪総称次元指定子≫も≪型実引数並び≫のいずれも含まれな
い場合,その一連の字句は≪型名≫とみなされる。≪非束縛型名≫の意味は,次のように決定される。
− 一連の字句を,個々の≪総称次元指定子≫を個々の≪型実引数≫と同じ数のコンマ及びキーワード
objectをもつ≪型実引数並び≫に置換することによって≪型名≫に変換する。
− その結果の≪型名≫を評価する。すべての型仮引数制約を無視する。
− ≪非束縛型名≫を,その結果である構築型に関連する非束縛総称型に解決する。
≪typeof式≫の結果は,上の手段で決定された非束縛総称型に対するSytem.Typeオブジェクトとす
る。
第3の形式の≪typeof式≫は,キーワードtypeof及び括弧で囲んだキーワードvoidとなる。この
形式の式の結果は,型が存在しないことを表現するSystem.Typeオブジェクトとなる。typeof ( void )
によって返される型オブジェクトは,どの型に対して返される型オブジェクトとも異なる。
注記 この特殊な型オブジェクトは,次のようなクラスライブラリにおいて役立つ。それは,言語の
メソッドについての自己反映を可能にし,そのようなメソッドでは,System.Typeのインス
タンスについて,voidメソッドを含む任意のメソッドの返却型を表す手段を必要とするクラ
スライブラリである。
例 次に例を示す。
using System;
class Test
{
static void Main() {
Type[] t = {
typeof(int),
typeof(System.Int32),
typeof(string),
typeof(double[]),
typeof(void)
};
for (int i = 0; i < t.Length; i++) {
Console.WriteLine(t[i].FullName);
}
197
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
このプログラムは次のように出力する。
System.Int32
System.Int32
System.String
System.Double[]
System.Void
intとSystem.Int32が,同じ型であることに注意する。
typeof演算子は,型仮引数(25.1.1参照)に対して利用できる。その結果は,その型仮引数に束縛さ
れた実行時の型に対するSystem.Typeオブジェクトとする。typeof演算子は,構築型(25.5参照)又
は非束縛総称型にも利用できる。非束縛総称型に対するSystem.Typeオブジェクトは,インスタンス型
に対するSystem.Typeオブジェクトとは同じではない。インスタンス型は実行時には常に閉構築型であ
り,そのSystem.Typeオブジェクトは,利用時の実行型実引数に依存する。その一方,非束縛総称型は
型実引数をもたない。
例 次に例を示す。
class X<T>
{
public static void PrintTypes(){
Console.WriteLine(typeof(T).FullName);
Console.WriteLine(typeof(X<T>).FullName);
Console.WriteLine(typeof(X<X<T>>).FullName);
Console.WriteLine(typeof(X<>).FullName);
}
}
class M
{
static void Main() {
X<int>.PrintTypes();
X<string>.PrintTypes();
}
}
このプログラムは,例えば,次のように出力する。
System.Int32
X̀1[[System.Int32, mscorlib, Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]]
X̀1[[X̀1[[System.Int32, mscorlib, Version=2.0.3600.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089]], CSharpEcmaExample,
Version=1.0.1717.35787, Culture=neutral, PublicKeyToken=null]]
198
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
X̀1
System.String
X̀1[[System.String, mscorlib, Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089]]
X̀1[[X̀1[[System.String, mscorlib, Version=2.0.3600.0,
Culture=neutral,
PublicKeyToken=b77a5c561934e089]], CSharpEcmaExample,
Version=1.0.1717.35787, Culture=neutral, PublicKeyToken=null]]
X̀1
typeof(X<>)の結果は,型実引数には依存しないが,typeof(X<T>)は型実引数に依存する
ことに注意する。
14.5.12
sizeof演算子
sizeof演算子は,指定された型の変数が保持する8ビット単位のバイト数を返す。sizeof演算子の
演算対象として指定された型は,非管理型(27.2参照)でなければならない。
≪sizeof式≫:
sizeof ( ≪非管理型≫ )
sizeof演算子によってint型の定数を生成するあらかじめ定義された型を,次の表に示す。
式
結果
sizeof(sbyte)
1
sizeof(byte)
1
sizeof(short)
2
sizeof(ushort)
2
sizeof(int)
4
sizeof(uint)
4
sizeof(long)
8
sizeof(ulong)
8
sizeof(char)
2
sizeof(float)
4
sizeof(double)
8
sizeof(bool)
1
sizeof(decimal)
16
他のすべての演算対象の型に関するsizeof演算子については27.5.8で規定する。
14.5.13
checked演算子及びunchecked演算子
checked演算子及びunchecked演算子は,整数型算術演算及び変換に対するけた(桁)あふれの検査
文脈を制御するために使用される。
≪checked式≫:
checked ( ≪式≫ )
≪unchecked式≫:
unchecked ( ≪式≫ )
checked演算子は,指定された式を検査文脈で評価し,unchecked演算子は,指定された式を非検査
文脈で評価する。与えられたけた(桁)あふれ検査文脈で指定された式が評価されることを除いては,≪
checked式≫又は≪unchecked式≫は,完全に≪括弧付き式≫(14.5.3参照)に対応する。
199
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
けた(桁)あふれ検査文脈は,checked文及びunchecked文(15.11参照)を用いて制御することも
できる。
次の演算は,checked及びuncheckedの演算子及び文で設定したけた(桁)あふれ検査文脈によって
影響を受ける。
− あらかじめ定義された単項演算子++及び--(14.5.9及び14.6.5参照)。ただし,演算対象が整数型又
は列挙型のとき。
− あらかじめ定義された単項演算子-(14.6.2参照)。ただし,演算対象が整数型のとき。
− あらかじめ定義された2項演算子+,-,*,及び/(14.7参照)。ただし,両方の演算対象が整数型又
は列挙型のとき。
− ある整数型又は列挙型から別の整数型又は列挙型へ,又はfloat若しくはdoubleから整数型又は
列挙型への明示的な数値変換(13.2.1参照)。
上の演算において生成された値が,目的の型の範囲を超えると,演算が実行される文脈によって,結果
の振る舞いが制御される。
− checked文脈においては,演算が定数式(14.15参照)ならば,コンパイル時エラーになる。そうで
なくて,実行時に演算がなされたのならば,System.OverflowExceptionが送出される。
− unchecked文脈においては,目的の型に収まらない上位ビットが破棄されて,結果が切り捨てられ
る。
非定数式(14.16参照)が,checked又はuncheckedの演算子又は文で囲まれていない場合は,(コン
パイラスイッチ及び実行環境設定のような)外部要因によってけた(桁)あふれ検査が指定されない限り,
省略時のけた(桁)あふれ検査文脈はuncheckedとする。
定数式(14.16参照)の場合,省略時のけた(桁)あふれ検査文脈は,常にcheckedとする。定数式が
明示的にunchecked文脈の中に置かれていない限り,式のコンパイル時評価においてけた(桁)あふれ
が発生すると,常に,コンパイル時エラーになる。
注記 開発者にとっては,(非検査モードもそうだが)検査モードを使ったコード実行は有用である。
また,デバッグのときには,特に要請がない限り,省略時のけた(桁)あふれ検査文脈は検査
されるのが妥当と考えられる。
例1 次の例を考える。
class Test
{
static readonly int x = 1000000;
static readonly int y = 1000000;
static int F() {
return checked(x * y);
// Throws OverflowException
}
static int G() {
return unchecked(x * y);
// Returns -727379968
}
static int H() {
return x * y;
// Depends on default
}
200
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
この例では,どの式もコンパイル時に評価されないので,コンパイル時エラーとはならない。
実行時,メソッドFは,例外System.OverflowExceptionを送出する。Gは,‒727379968
(範囲を超える結果の下位32ビット)を返す。メソッドHの振る舞いは,コンパイル時に決
定される省略時のけた(桁)あふれ検査文脈に依存するが,Fと同じになるかGと同じになる
かである。
例2 次の例を考える。
class Test
{
const int x = 1000000;
const int y = 1000000;
static int F() {
return checked(x * y);
// Compile error, overflow
}
static int G() {
return unchecked(x * y);
// Returns -727379968
}
static int H() {
return x * y;
// Compile error, overflow
}
}
F及びHの定数式はchecked文脈で評価されるため,それらの定数式を評価したときに発
生するけた(桁)あふれは,コンパイル時エラーになる。けた(桁)あふれは,Gの定数式の
評価でも発生するが,これはunchecked文脈で評価されるために,けた(桁)あふれは報告
されない。
checked演算子及びunchecked演算子は,字句“(”及び“)”の内側の演算に対するけた(桁)あ
ふれ検査文脈に対してだけ影響を与える。対象となる式の評価によって呼び出される関数メンバには,影
響を与えない。
例 次の例を考える。
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
return checked(Multiply(1000000, 1000000));
}
}
Fの中で用いているcheckedは,Multiplyにおけるx * yの評価には影響を与えないため,
x * yは,省略時のけた(桁)あふれ検査文脈で評価される。
201
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
unchecked演算子は,16進表記で符号付き整数型の定数を記述するときに役立つ。
例 次の例を考える。
class Test
{
public const int AllBits = unchecked((int)0xFFFFFFFF);
public const int HighBit = unchecked((int)0x80000000);
}
上の16進定数は共に,型uintである。これらの定数は,intの範囲外なので,unchecked
演算子なしでは,intへのキャストでコンパイル時エラーになる。
注記 checked及びuncheckedの演算子及び文は,プログラマが数値計算の一部を制御することを
可能にする。ただし,一部の数値演算子の振る舞いは,演算対象のデータ型に依存する。例え
ば,二つの10進実数値を乗算した結果は,たとえ明示的にunchecked宣言した構築要素内で
も,常にけた(桁)あふれによる例外が発生する。一方,二つの浮動小数点数を乗算した結果
は,たとえ明示的にchecked宣言した構築要素内でも,けた(桁)あふれによる例外が発生
しない。その他の演算子は,省略時であれ明示的であれ,検査文脈に影響されない。プログラ
マの便宜のために,明示的なchecked又はuncheckedの演算子又は文による文脈内の算術
式が,指定された検査モードの影響を受けないとき,コンパイラは警告を発行するように推奨
する。この警告は要件ではないので,コンパイラは,そのような警告の発行を柔軟に決定して
よい。
14.5.14
省略時値式
省略時値式は,型の省略時の値を取得するために使われる。通常,省略時値式は,型仮引数の値型又は
参照型かが分からないので,型仮引数(25.1.1参照)に対して使われる。型仮引数が参照型であることが
分からない限り,null型から型仮引数への変換は存在しない(25.7参照)。
≪省略時値式≫:
default ( ≪型≫ )
≪省略時値式≫内の≪型≫が,実行時に参照型に評価される場合,その結果は,その型に変換された
nullとする。≪省略時値式≫内の≪型≫が,実行時に値型に評価される場合,その結果は,その≪値型
≫の省略時の値とする(11.1.2参照)。
14.5.15
無名メソッド
≪無名メソッド式≫は,無名メソッドを定義し,そのメソッドを参照している値を評価する。
≪無名メソッド式≫:
delegate ≪無名メソッド呼出し情報≫opt ≪ブロック≫
≪無名メソッド呼出し情報≫:
( ≪無名メソッド仮引数並び≫opt )
≪無名メソッド仮引数並び≫:
≪無名メソッド仮引数≫
≪無名メソッド仮引数並び≫ , ≪無名メソッド仮引数≫
≪無名メソッド仮引数≫:
≪仮引数修飾子≫opt ≪式≫ ≪識別子≫
≪無名メソッド式≫の≪型≫は,委譲型であり,その式の文脈から推論される。推論規則は13.5で規定
202
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
する。
≪無名メソッド式≫は≪委譲生成式≫で使える。他のすべての正当な≪無名メソッド式≫の使用は,13.5
で定義した暗黙変換に依存する。
≪無名メソッド式≫は,仮引数,局所変数及び定数に対する新しい宣言空間,並びにラベルに対する新
しい宣言空間を定義する。
14.5.15.1
無名メソッド呼出し情報
省略可能な≪無名メソッド呼出し情報≫は,無名メソッドの仮引数の名前及び型を定義する。無名メソ
ッドの仮引数の有効範囲は,その≪ブロック≫とする。無名メソッドの仮引数の名前が,≪無名メソッド
式≫を含んでいる有効範囲内の局所変数,局所定数又は仮引数の名前と一致する場合,コンパイル時エラ
ーとする。
≪無名メソッド式≫が≪無名メソッド呼出し情報≫をもつ場合,互換性のある委譲型の集合は,同じ仮
引数型,修飾子をもち,同じ順番の仮引数をもつ委譲型に限られる(13.5参照)。メソッド群とは対照的に,
無名メソッドの仮引数型の反変は扱わない(14.5.10.3参照)。≪無名メソッド式≫が≪無名メソッド呼出
し情報≫をもたない場合,互換性のある委譲型の集合は,out仮引数をもたない委譲型に限られる。
≪無名メソッド呼出し情報≫は,属性又は≪仮引数配列≫を含めることができない。しかし,≪無名メ
ソッド呼出し情報≫は,≪仮引数並び≫が≪仮引数配列≫を含む委譲型と互換性をもつことができる。
14.5.15.2
無名メソッドブロック
≪無名メソッド式≫の≪ブロック≫は,次の規則に従う。
− 無名メソッドが呼出し情報を含む場合,その呼出し情報に指定された仮引数は,≪ブロック≫で利用
可能とする。無名メソッドが呼出し情報を含まない場合,仮引数をもった委譲型に変換できるが(13.5
参照),≪ブロック≫内でそれらの仮引数にはアクセスできない。
− 最も直近の囲んでいる無名メソッドの呼出し情報(があれば)で指定されたref 仮引数又はout仮
引数を除いて,≪ブロック≫でref仮引数又はout仮引数にアクセスする場合,コンパイル時エラ
ーとする。
− thisの型が構造体型の場合,≪ブロック≫でのthisへのアクセスはコンパイル時エラーとする。
これは,アクセスが(this.xのように)明示的であっても,(xが構造体のインスタンスメンバのと
きのxのように)暗黙的であっても,常に真である。この規則は,メンバ検索の結果が構造体のメン
バであるどうかに関係なく,単に,そういったアクセスを禁止する。
− ≪ブロック≫では無名メソッドの外変数(14.5.15.3参照)へのアクセスができる。外変数のアクセス
は,≪無名メソッド式≫が評価される時点で活性状態にある変数のインスタンスを参照する。
− ≪ブロック≫に,≪ブロック≫の外側,又は,無名メソッドを含んだ≪ブロック≫の内部を遷移先と
したgoto文,break文又はcontinue文を含んでいる場合,コンパイル時エラーとする。
− ≪ブロック≫内のreturn文は,囲んでいる関数メンバからではなく,最も直近の囲んでいる無名メ
ソッドの呼出しから制御を戻す。return文を指定した式は,最も直近の囲んでいる≪無名メソッド
式≫から変換される委譲型と互換性をもたなければならない。
≪無名メソッド式≫の評価及び呼出しを通さずに,無名メソッドの≪ブロック≫を評価する方法がある
かどうかについては,明確に規定しない。特に,コンパイラは,名前をもつ一つ以上のメソッド又は型を
合成することによって,無名メソッドの実装を選択してもよい。そういった合成された要素の名前は,実
装で予約された空間内にあるものとしなければならない。つまり,その名前は二つの連続した下線文字を
含めなければならない。
203
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
14.5.15.3
外変数
≪無名メソッド式≫を含む有効範囲の任意の局所変数,値仮引数又は仮引数配列を,その≪無名メソッ
ド式≫の外変数という。クラスのインスタンス関数メンバにおいて,thisの値は値仮引数とみなされ,
その関数メンバ内に含まれる≪無名メソッド式≫の外変数とする。
14.5.15.3.1 外変数の捕そく(捉)
外変数が無名メソッドによって参照されるとき,その外変数は,その無名メソッドによって捕そく(捉)
されたという。通常,局所変数の生存期間は,その変数が関連するブロック又は文の実行に限定される
(12.1.7参照)。しかし,捕そく(捉)された外変数の生存期間は,少なくとも,その無名メソッドが参照
している委譲がごみ集め可能となるまで延長される。
例 次の例を考える。
using System;
delegate int D();
class Test
{
static D F() {
int x = 0;
D result = delegate { return ++x; }
return result;
}
static void Main() {
D d = F();
Console.WriteLine(d());
Console.WriteLine(d());
Console.WriteLine(d());
}
}
このコードでは,無名メソッドによって局所変数xが捕そく(捉)され,xの生存期間は,少
なくとも,Fから返却される委譲がごみ集め可能となるまで延長される。無名メソッドの呼出し
のたびに,同じインスタンスであるxに対する演算を行うので,次のように出力される。
1
2
3
14.5.15.3.2 局所変数のインスタンス化
局所変数は,実行が,その変数の有効範囲に入ったときにインスタンス化されたとみなされる。
例 次の例を考える。次のメソッドが呼び出されたとき,局所変数xはインスタンス化され,繰返し
のたびに1回づつ,合計3回初期化される。
static void F() {
for(int I = 0; I < 3; i++) {
int x = i * 2 + 1;
...
204
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
しかし,xの宣言を繰返しの外側に移動させると,xは単一のインスタンス化される。
static void F() {
int x;
for(int I = 0; I < 3; i++) {
int x = i * 2 + 1;
...
}
}
通常,局所変数が何回インスタンス化されるかを正確に観察する手段はない。なぜならば,インスタン
ス化の生存期間は互いに重ならないので,個々のインスタンス化が同じ記憶域を利用できるからである。
しかし,無名メソッドに局所変数を捕そく(捉)させると,インスタンス化の結果が明らかになる。
例 次の例を考える。
using System;
delegate int D();
class Test
{
static D F() {
D[] result = new D[3];
for(int i = 0; I < 3; i++) {
int x = i * 2 + 1;
result[i] = delegate { Console.WriteLine(x); };
}
return result;
}
static void Main() {
foreach(D d in F()) d();
}
}
このコードは,次のように出力される。
1
3
5
しかし,xの宣言を繰返しの外側に移動させると,
static D F() {
D[] result = new D[3];
int x;
for(int i = 0; I < 3; i++) {
x = i * 2 + 1;
205
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
result[i] = delegate { Console.WriteLine(x); };
}
return result;
}
次のように出力される。
5
5
5
最後のコードでのFに対して生成された三つの委譲は,等価演算子(14.9.8参照)に従って等
価となることに注意する。さらに,コンパイラは三つのインスタンス化を一つの委譲インスタン
スに最適化することが(要求はされていないが)許されている(14.5.15.4参照)。
無名メソッドの複数の委譲において,捕そく(捉)された変数の幾つかを共有し,残りを別々にもつこ
とができる。
例 次の例を考える。
static D F() {
D[] result = new D[3];
int x;
for(int i = 0; I < 3; i++) {
int y = 0;
result[i] = delegate { Console.WriteLine("{0} {1}", ++x,
++y); };
}
return result;
}
このコードは,次のように出力される。
1 1
2 1
3 1
別々の無名メソッドは,外変数の同じインスタンスを捕そく(捉)できる。
例 次の例を考える。
using System;
delegate void Setter(int value);
delegate int Getter()
class Test
{
static void Main() {
int x = 0;
Setter s = delegate(int value) { x = value; };
Getter g = delegate { return x; };
206
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
s(5);
Colsole.WriteLine(g());
s(10);
Colsole.WriteLine(g());
}
}
このコードは,二つの無名メソッドが局所変数xの同じインスタンスを捕そく(捉)する。よ
って,その変数を通して情報をやりとりできる。このコードでは,次のような出力になる。
5
10
14.5.15.4
無名メソッドの評価
≪無名メソッド式≫の実行時の評価は,その無名メソッド及び評価時に活性状態にある捕そく(捉)さ
れた外変数の集合(空の場合もある。)を参照する委譲インスタンスを生成する。≪無名メソッド式≫結果
である委譲が呼び出されると,その無名メソッドの本体が実行される。本体のコードは,委譲によって参
照される捕そく(捉)された外変数の集合を使って実行される。
≪無名メソッド式≫から生成された委譲の呼出し並びは,単一の入口を含む。委譲の厳密な対象オブジ
ェクト及び対象メソッドは規定しない。特に,委譲の対象オブジェクトがnull,囲んでいる関数メンバ
のthisの値,又は別のオブジェクトであるかどうかは規定しない。
同じ(空の場合もある。)捕そく(捉)された外変数インスタンスの集合をもつ意味的に同一の≪無名メ
ソッド式≫の評価が,同じ委譲インスタンスを返却することは要求はしないが,許可する。ここで使われ
ている“意味的に同一”という用語は,無名メソッドの実行が生成され,そのすべての場合において,同
じ実引数に対して同じ効果を与えることを意味する。
例 この規則は,次のようにコードを最適化することを許可する。
delegate double Function(double x);
class Test
{
static double[] Apply(double[] vector, Function fund) {
double[] result = new double[vector.Length];
for(int i = 0; i < vector.Length; i++) {
result[i] = func(vector[i]);
}
return result;
}
static void F(double[] vx, double[] vy) {
double[] rx = Apply(vx, delegate(double x) {
return Math.Sin(x);
});
double[] ry = Apply(vy, delegate(double y) {
return Math.Sin(y);
});
207
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
この例は,二つの無名メソッドの委譲が,同じ(空の)捕そく(捉)された外変数の集合をも
ち,無名メソッドが意味的に同一であるので,コンパイラは同じ対象メソッドを参照する委譲を
もたせてもよい。実際,コンパイラは両方の無名メソッド式から同じ委譲インスタンスを返却さ
せてもよい。
14.5.15.5
実装例
注記 14.5.15.5では,無名メソッドを標準C#の構成要素で可能な実装について示す。ここで示した実
装を強制するものでもないし,この実装が唯一無二なものでもない。
異なる特徴をもった無名メソッドを含む例を幾つか示す。個々の例では,標準C#が提供する
構成要素だけを使って翻訳されたコードを提供する。次の例では,識別子Dは委譲型を表現し
ている。
public delegate void D();
最も単純な無名メソッドの形式は,捕そく(捉)された外変数がないものである。
class Test
{
static void F() {
D d = delegate { Console.WriteLine("test"); };
}
}
これは,次のような,無名メソッドのコードが記述された場所に,コンパイラが生成した静
的メソッドを参照する委譲インスタンスに翻訳できる。
class Test
{
static void F() {
D d = new D(̲̲Method1);
}
static void ̲̲Method1 () {
Console.WriteLine("test");
}
}
次の例は,thisのインスタンスメンバを参照する無名メソッドである。
class Test
{
int x;
void F() {
D d = delegate { Console.WriteLine(x); };
}
}
これは,次のような,無名メソッドのコードを含んだ,コンパイラが生成したインスタンス
メソッドに翻訳できる。
208
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class Test
{
int x;
void F() {
D d = new D(̲̲Method1);
}
void ̲̲Method1 () {
Console.WriteLine(x);
}
}
次の例は,局所変数を捕そく(捉)する無名メソッドである。
class Test
{
void F() {
int y = 123;
D d = delegate { Console.WriteLine(y); };
}
}
局所変数の生存期間は,無名メソッドの委譲の生存期間まで少なくとも延長されなければな
らない。これは,局所変数をコンパイラが生成したクラスのフィールドに“もち上げる”こと
で実現できる。局所変数のインスタンス化(14.5.15.3.2参照)が,コンパイラが生成したクラ
スのインスタンスの生成に対応し,局所変数のアクセスは,コンパイラが生成したクラスのイ
ンスタンスのフィールドのアクセスに対応する。さらに,無名メソッドは,コンパイラが生成
したクラスのインスタンスメソッドになる。
class Test
{
void F() {
̲̲Locals1 ̲̲locals1 = new ̲̲Locals1();
̲̲locals1.y = 123;
D d = new D(̲̲locals1.̲̲Method1);
}
class ̲̲Locals1
{
public int y;
public void ̲̲Method1 () {
Console.WriteLine(y);
}
}
}
最後に,次のように,this及び異なる生存期間をもつ二つの局所変数を捕そく(捉)する
209
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
無名メソッドを示す。
class Test
{
int x;
void F() {
int y = 123;
for(int i = 0; i < 10; i++) {
int z = i * 2;
D d = delegate { Console.WriteLine(x + y + z); };
}
}
}
ここで,コンパイラが生成したクラスは,局所変数が捕そく(捉)され個々の文ブロックで
生成される。その局所変数は異なるブロック内にあり,独立した生存期間をもつことができる。
内側の文ブロックに対してコンパイラが生成したクラス̲̲Locals2のインスタンスは,局所
変数z及び̲̲Local1のインスタンスを参照するフィールドを含んでいる。外側の文ブロック
に対してコンパイラが生成したクラス̲̲Locals1のインスタンスは,局所変数y及び,囲ん
でいる関数メンバのthisを参照するフィールドを含んでいる。これらのデータ構造では,
̲̲Locals2のインスタンスを通して,補足された外変数のすべてに到達可能であり,無名メ
ソッドのコードは,そのクラスのインスタンスメソッドとして実装できる。
class Test
{
int x;
void F() {
̲̲Locals1 ̲̲locals1 = new ̲̲Locals1();
̲̲locals1.this = this;
̲̲locals1.y = 123;
for(int i = 0; i < 10; i++) {
̲̲Locals2 ̲̲locals2 = new ̲̲Locals2();
̲̲locals2.̲̲locals1 = ̲̲locals1;
̲̲locals2.z = i * 2;
D d = new D(̲̲locals2.̲̲Method1);
}
}
class ̲̲Locals1
{
public Test ̲̲this;
public int y;
}
class ̲̲Locals2
210
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public ̲̲Locals1 ̲̲locals1;
public int z;
public void ̲̲Method1() {
Console.WriteLine(̲̲locals1.̲̲this.x + ̲̲locals1.y +
z);
}
}
}
14.6 単項式
≪単項式≫:
≪一次式≫
+ ≪単項式≫
- ≪単項式≫
! ≪単項式≫
~ ≪単項式≫
≪前置増加式≫
≪前置減少式≫
≪キャスト式≫
14.6.1 単項プラス演算子
形式+xの演算に対して,演算子実装を選択するために,単項演算子多重定義解決(14.2.3参照)が適用
される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却値の型
とする。あらかじめ定義された単項プラス演算子は,次による。
int operator +(int x);
uint operator +(uint x);
long operator +(long x);
ulong operator +(ulong x);
float operator +(float x);
double operator +(double x);
decimal operator +(decimal x);
これらの演算子の各々に対して,結果は,単に演算対象の値とする。
上で定義したあらかじめ定義された非もち上げの単項プラス演算子のもち上げ形式(14.2.7参照)も,
あらかじめ定義されている。
14.6.2 単項マイナス演算子(符号反転)
形式-xの演算に対して,演算子実装を選択するために,単項演算子多重定義解決(14.2.3参照)が適用
される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却値の型
とする。あらかじめ定義された符号反転演算子(単項マイナス演算子)は,次による。
− 整数の符号反転
int operator ‒(int x);
long operator ‒(long x);
211
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
結果は,xを0から減算することによって計算される。checked文脈では,xの値がint又はlong
の最も小さい負の場合(それぞれ,−231又は−263),System.OverflowExceptionが送出される。
unchecked文脈では,xの値がint又はlongの最も小さい負の場合,結果はその同じ値とし,けた(桁)
あふれは報告されない。
符号反転演算子の演算対象が型uintの場合,それは型longに変換され,結果の型はlongになる。
例外は,10進整数のリテラル(9.4.4.2参照)として書いた場合のint値−2147483648 (−231) を許容する
規則とする。
− ulongの符号反転はエラーとする。
long operator ‒(ulong x);
単項演算子多重定義解決(14.2.3参照)による,この演算子の選択は,常に,コンパイル時エラー
とする。必然的に,符号反転演算子の演算対象がulong型の場合,コンパイル時エラーになる。例外
は,10進整数のリテラル(9.4.4.2参照)として書いた場合のlong値−9223372036854775808 (−263) を
許容する規則とする。
− 浮動小数点数の符号反転
float operator ‒(float x);
double operator ‒(double x);
結果は,xの符号を反転させた値とする。xがNaNの場合,結果もNaNとする。
− 10進実数の符号反転
decimal operator ‒(decimal x);
結果は,xを0から減算することによって計算される。
10進実数の符号反転は,型System.Decimalの単項マイナス演算子を用いた場合と等価とする。
上で定義したあらかじめ定義された非もち上げの単項マイナス演算子のもち上げ形式(14.2.7参照)も,
あらかじめ定義されている。
14.6.3 論理否定演算子
形式!xの演算に対して,演算子実装を選択するために,単項演算子多重定義解決(14.2.3参照)が適用
される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却値の型
とする。あらかじめ定義された論理否定演算子は,次の一つだけ存在する。
bool operator !(bool x);
この演算子は,演算対象の論理否定を計算する。演算対象がtrueの場合,結果はfalseとする。演算
対象がfalseの場合,結果はtrueとする。
上で定義したあらかじめ定義された非もち上げの単項マイナス演算子のもち上げ形式(14.2.7参照)も,
あらかじめ定義されている。
14.6.4 ビット単位の補数演算子
形式~xの演算に対して,演算子実装を選択するために,単項演算子多重定義解決(14.2.3参照)が適用
される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却値の型
とする。あらかじめ定義されたビット単位の補数演算子は,次による。
int operator ~(int x);
uint operator ~(uint x);
long operator ~(long x);
ulong operator ~(ulong x);
212
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
これらの演算子の各々に対して,結果は,xのビット単位の補数とする。
すべての列挙型Eは,暗黙に,次のビット単位の補数演算子を提供する。
E operator ~(E x);
基礎とする型をUとする列挙型Eの式をxとすると,~xを評価した結果は,(E) (~(U)x)を評価した
ものと同じとする。この演算子は,演算対象の型が列挙型Eのとき,単項演算子多重定義解決によってだ
け考慮される。
上で定義したあらかじめ定義された非もち上げのビット単位の補数演算子のもち上げ形式(14.2.7参照)
も,あらかじめ定義されている。
14.6.5 前置増加演算子及び前置減少演算子
≪前置増加式≫:
++ ≪単項式≫
≪前置減少式≫:
-- ≪単項式≫
前置増加演算子又は前置減少演算子の演算対象は,変数,特性アクセス又は添字子アクセスとして分類
される式でなければならない。その演算の結果は,演算対象と同じ型の値とする。
前置増加演算子又は前置減少演算子の演算対象が特性アクセス又は添字子アクセスの場合,その特性又
は添字子は,getアクセス子及びsetアクセス子の両方をもたなければならない。そうでない場合には,
コンパイル時エラーになる。
演算子実装選択には,単項演算子多重定義解決(14.2.3参照)が適用される。あらかじめ定義された演
算子++ 及びあらかじめ定義された演算子-- は,sbyte,byte,short,ushort,int,uint,long,
ulong,char,float,double,decimal及び任意の列挙型に対して存在する。これらのあらかじめ定
義された演算子の返却型は,その演算対象の型と同じとする。あらかじめ定義された演算子++は,演算対
象に1を加算することによって生成される値を返し,あらかじめ定義された--演算子は,演算対象から1
を減算することによって生成される値を返す。checked文脈では,この加算及び減算の結果が,返却型の
範囲を超える場合であって,返却型が整数型又は列挙型の場合,System.OevrflowExceptionが送出
される。
選択された単項演算子の返却型から,≪一次式≫の型への暗黙の変換が存在しなければならない。そう
でなければ,コンパイル時エラーとする。
形式++x又は形式--xの前置増加演算又は前置減少演算の実行時処理は,次の手順からなる。
− xが変数として分類される場合
・ xは,変数を生成するものと評価される。
・ xの値を選択された演算子の演算対象の型に変換し,その値を実引数として演算子が呼び出される。
・ 演算子からの返却値をxの型に変換する。結果の型は,xの評価によって得られる場所に格納され,
それが演算の結果になる。
− xが特性アクセス又は添字子アクセスとして分類される場合
・ xに関連するインスタンス式(xがstaticではない場合)及び実引数並び(xが添字子アクセス
の場合)が評価され,その結果が,それに続くgetアクセス子及びsetアクセス子の呼出しで使
用される。
・ xのgetアクセス子が呼び出される。
・ getアクセス子が返す値を選択された演算子の演算対象の型に変換し,その値を実引数として,演
213
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
算子が呼び出される。
・ アクセス子演算子が返す値をxの型に変換する。この値をvalue実引数としてxのsetアクセス
子が呼び出される。また,その値が演算の結果になる。
演算子++及び演算子--は,後置記法(14.5.9参照)も提供する。x++又はx--の結果は,演算の前のx
の値とし,++x又は--xの結果は,演算の後のxの値とする。いずれの場合も,xそれ自体は,演算の後
の値をもつ。
operator ++又はoperator --の実装は,後置記法又は前置記法のいずれかを用いて呼び出すことが
できる。二つの記法に対して,演算子を別々に実装することはできない。
上で定義したあらかじめ定義された非もち上げの前置増加演算子及び前置減少演算子のもち上げ形式
(14.2.7参照)も,あらかじめ定義されている。
14.6.6 キャスト式
≪キャスト式≫は,式を与えられた型へと明示的に変換するために用いる。
≪キャスト式≫:
( ≪型≫ ) ≪単項式≫
Tを型としEを単項式とする(T)Eという形式の≪キャスト式≫は,Eの値を型Tにする明示的な変換
(13.2参照)を実行する。Eの型をTにする明示的な変換が存在しない場合,コンパイル時エラーになる。
存在する場合には,明示的な変換によって生成される値が結果になる。結果は常に,たとえEが変数を示
す場合であっても,値として分類される。
≪キャスト式≫のための文法には,構文的にあいまいさがある。
例 例えば,式(x)‒yは,≪キャスト式≫(‒yの型xへのキャスト)としても,≪括弧付き式≫と
組み合わされた≪加減式≫(値x ‒ yを計算する式)としても解釈できる。
≪キャスト式≫のあいまいさを解決するために,次の規則が存在する。括弧の中にある一つ以上の≪字
句≫(9.4参照)の列は,次の条件の少なくとも一つが満たされている場合にだけ,≪キャスト式≫の開始
とみなす。
− 字句の列が,≪型≫について正しい文法になっているが,≪式≫については正しい文法になっていな
い。
− 字句の列が,≪型≫について正しい文法になっており,閉じの括弧の直後の字句が,字句“~”,字句
“!”,字句“(”,≪識別子≫(9.4.2参照),≪リテラル≫(9.4.4参照),又はas及びisを除く何ら
かの≪キーワード≫(9.4.3参照)になっている。
この規則で使われている“正しい文法”という表現は,字句の列が特定の文法生成規則に適合しなけれ
ばならない,ということだけを意味する。(列を)構成する識別子の実際の意味については,特に考慮しな
い。
例 例えば,x及びyが識別子の場合,x.yは,それが実際には型を表示しない場合であっても,型
について正しい文法とする。
注記 あいまいさ解消の規則から,x及びyが識別子の場合,(x)y,(x)(y)及び(x)(-y)は≪キャ
スト式≫であるが,(x)-yは,たとえxが型を識別する場合であっても,≪キャスト式≫には
ならない。しかし,xが(intといった)あらかじめ定義された型を識別するキーワードの場
合,これらの四つの形式は,すべて,≪キャスト式≫になる。これは,そのようなキーワード
は,それ自体では式になることはあり得ないことによる。
14.7 算術式
214
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
演算子*,/,%,+及び-を,算術演算子という。
≪乗除式≫:
≪単項式≫
≪乗除式≫ * ≪単項式≫
≪乗除式≫ / ≪単項式≫
≪乗除式≫ % ≪単項式≫
≪加減式≫:
≪乗除式≫
≪加減式≫ + ≪乗除式≫
≪加減式≫ ‒ ≪乗除式≫
14.7.1 乗算演算子
x * yという形式の演算に対しては,演算子実装を選択するために,2項演算子多重定義解決(14.2.4
参照)が適用される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子
の返却値の型とする。
あらかじめ定義された乗算演算子は,次による。これらの演算子は,すべて,xとyの積を計算する。
− 整数の乗算
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
void operator *(long x, ulong y);
void operator *(ulong x, long y);
voidを返却する演算子は,常に,コンパイル時エラーとする。したがって,一方の演算対象がlong
で,もう一方の演算対象はulongの場合,エラーとする。
checked文脈では,積が結果の型の値域外となる場合,System.OverflowExceptionが送出さ
れる。unchecked文脈では,けた(桁)あふれは報告されず,結果の型の値域外の有効上位ビット
が破棄される。
− 浮動小数点数の乗算
float operator *(float x, float y);
double operator *(double x, double y);
積は,IEC 60559の算術の規則に従って計算される。次の表は,非0の有限値,0,無限大及びNaN
の可能な組合せの結果を一覧として示す。表では,x及びyは,正の有限値とする。zは,x * yの
結果とし,表現可能な最も近い値に丸められる。結果が目的の型に対して大き過ぎる場合,zは無限
大とする。結果が目的の型に対して小さ過ぎる場合,丸めによってzは0なることがある。
+y
‒y
+0
‒0
+∞
+∞
NaN
+x
+z
‒z
+0
‒0
+∞
-∞
NaN
‒x
‒z
+z
‒0
+0
-∞
+∞
NaN
+0
+0
‒0
+0
‒0
NaN
NaN
NaN
‒0
‒0
+0
‒0
+0
NaN
NaN
NaN
+∞
+∞
‒∞
NaN
NaN
+∞
‒∞
NaN
‒∞
‒∞
+∞
NaN
NaN
‒∞
+∞
NaN
215
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
− 10進実数の乗算
decimal operator *(decimal x, decimal y);
結果の値が10進書式で表現するには大き過ぎる場合,例外System.OverflowExceptionが送出
される。丸めによって,いずれかの演算対象が0でない場合でも,結果が0になることがある。丸め
を行う前の結果の小数部けた数は,二つの演算対象の小数部けた数の和とする。
10進実数の乗算は,型System.Decimalの乗算演算子を用いたものと同等とする。
上で定義したあらかじめ定義された非もち上げの乗算演算子のもち上げ形式(14.2.7参照)も,あらか
じめ定義されている。
14.7.2 除算演算子
x / yという形式の演算に対して,演算子実装を選択するために,2項演算子多重定義解決(14.2.4参
照)が適用される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の
返却値の型とする。
あらかじめ定義された除算演算子は,次による。演算子は,すべて,xとyの商を計算する。
− 整数の除算
int operator /(int x, int y);
uint operator /(uint x, uint y);
long operator /(long x, long y);
ulong operator /(ulong x, ulong y);
void operator /(long x, ulong y);
void operator /(ulong x, long y);
voidを返却する演算子は,常に,コンパイル時エラーとする。したがって,一方の演算対象がlong
で,もう一方の演算対象はulongの場合,エラーとする。
右演算対象の値が0の場合,System.DivideByZeroExceptionが送出される。
除算は,その結果を0に向かって丸め,結果の絶対値は,二つの演算対象の商の絶対値より小さい最大
の可能な整数とする。二つの演算対象が同じ符号をもつ場合,結果は0又は正とし,二つの演算対象が反
対の符号をもつ場合,結果は0又は負とする。
左演算対象がint又はlongの負の最も小さい値(それぞれ,−231又は−263)であって,右演算対象
が−1の場合,けた(桁)あふれが発生する。checked文脈では,これによって
System.ArithmeticException(又はその下位クラス)が送出される。unchecked文脈では,
System.ArithmeticException(又はその下位クラス)が送出されるか,又はけた(桁)あふれが報
告なしに発生し結果の値を左演算対象の値とする。いずれをとるかは,実装定義とする。
− 浮動小数点数の除算
float operator /(float x, float y);
double operator /(double x, double y);
商は,IEC 60559の算術の規則に従って計算される。次の表は,非0の有限値,0,無限大及びNaN
のすべての可能な組合せを一覧として示す。表では,x及びyは正の有限値とする。zは,x / yの
結果とし,表現可能な最も近い値に丸められる。結果が目的の型に対して大き過ぎる場合,zは無限
大とする。結果が目的の型に対して小さ過ぎる場合,丸めによってzは0になることがある。
216
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
+y
‒y
+0
‒0
+∞
‒∞
NaN
+x
+z
‒z
+∞
‒∞
+0
‒0
NaN
‒x
‒z
+z
‒∞
+∞
‒0
+0
NaN
+0
+0
‒0
NaN
NaN
+0
‒0
NaN
‒0
‒0
+0
NaN
NaN
‒0
+0
NaN
+∞
+∞
‒∞
+∞
‒∞
NaN
NaN
NaN
‒∞
‒∞
+∞
‒∞
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
− 10進実数の除算
decimal operator /(decimal x, decimal y);
右演算対象の値が0の場合,System.DivideByZeroExceptionが送出される。結果の値が10
進書式で表現するには大き過ぎる場合,例外System.OverflowExceptionが送出される。丸めに
よって,最初の演算対象が0でなくても,結果が0になることがある。丸めを行う前の結果の小数部
けた数は,厳密な結果に等しい結果を保持する最小の小数部けた数とする。
10進実数の除算は,型System.Decimalの除算演算子を用いたものと同等とする。
上で定義したあらかじめ定義された非もち上げの除算演算子のもち上げ形式(14.2.7参照)も,あらか
じめ定義されている。
14.7.3 剰余演算子
x % yという形式の演算に対して,演算子実装を選択するために,2項演算子多重解決(14.2.4参照)
が適用される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却
値の型とする。
あらかじめ定義された剰余演算子は,次による。演算子は,すべて,xとyの除算の剰余を計算する。
− 整数の剰余
int operator %(int x, int y);
uint operator %(uint x, uint y);
long operator %(long x, long y);
ulong operator %(ulong x, ulong y);
void operator %(long x, ulong y);
void operator %(ulong x, long y);
voidを返却する演算子は,常に,コンパイル時エラーとする。したがって,一方の演算対象がlong
で,もう一方の演算対象はulongの場合,エラーとする。
x % yの結果は,x ‒ (x / y) * yによって生成される値とする。yが0の場合,
System.DivideByZeroExceptionが送出される。
左演算対象がint又はlongの負の最も小さい値(それぞれ,−231又は−263)であって,右演算
対象が−1の場合,System.ArithmeticException(又はその下位クラス)が送出されるかどうか
は,実装定義とする。規格合致の実装は,x / yが例外を発生しない条件のとき,x % yも例外を送
出してはならない。
− 浮動小数点数の剰余
float operator %(float x, float y);
double operator %(double x, double y);
217
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
次の表は,0でない有限値,0,無限大及びNaNのすべての可能な組合せを一覧として示す。表で
は,x及びyは,正の有限値とする。Zは,x % yの結果とし,x ‒ n * yとして計算され,表現
可能な最も近い値に丸められる。ここで,nは,x / y以下の最大の可能な整数とする。剰余を計算
するこの手法は,整数を演算対象とする場合に使用されるものと類似するが,(nをx / yに最も近
い整数とする)IEC 60559の定義とは異なっている。
+y
‒y
+0
‒0
+∞
‒∞
NaN
+x
+z
+z
NaN
NaN
X
x
NaN
‒x
‒z
‒z
NaN
NaN
‒x
‒x
NaN
+0
+0
+0
NaN
NaN
+0
+0
NaN
‒0
‒0
‒0
NaN
NaN
‒0
‒0
NaN
+∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
‒∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
− 10進実数の剰余
decimal operator %(decimal x, decimal y);
右演算対象の値が0の場合,System.DivideByZeroExceptionが送出される。
System.ArithmeticException(又はその下位クラス)が送出されるときは実装定義とする。規
格合致の実装は,x / yが例外を発生しない条件のとき,x % yも例外を送出してはならない。丸め
を行う前の結果の小数部けた数は,二つの演算対象の小数部けた数のうち大きいほうとし,結果の符
号は,0でない場合は,xと同じとする。
10進実数の剰余は,型System.Decimalの剰余演算子と同等とする。
上で定義したあらかじめ定義された非もち上げの剰余演算子のもち上げ形式(14.2.7参照)も,あらか
じめ定義されている。
14.7.4 加算演算子
x + yの形式の演算子に対して,演算子実装を選択するために,2項演算子多重定義解決(14.2.4参照)
が適用される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却
値の型とする。
あらかじめ定義された加算演算子は,次による。数値型及び列挙型については,あらかじめ定義された
加算演算子は,二つの演算対象の和を計算する。演算対象の片方又は両方が型stringの場合は,演算対
象の文字列表現を連結する。
− 整数の加算
int operator +(int x, int y);
uint operator +(uint x, uint y);
long operator +(long x, long y);
ulong operator +(ulong x, ulong y);
void operator +(long x, ulong y);
void operator +(ulong x, long y);
voidを返却する演算子は,常に,コンパイル時エラーとする。したがって,一方の演算対象がlong
で,もう一方の演算対象はulongの場合,エラーとする。
218
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
checked文脈では,和が結果の型の値域外の場合,System.OverflowExceptionが送出される。
unchecked文脈では,けた(桁)あふれは報告されず,結果の型の値域外の有意の上位ビットは,
破棄される。
− 浮動小数点数の加算
float operator +(float x, float y);
double operator +(double x, double y);
和は,IEC 60559の算術の規則に従って計算される。次の表は,非0の有限値,0,無限大及びNaN
のすべての可能な組合せの結果を一覧として示す。表では,x及びyは,非0の有限値とし,zは,x + y
の結果とし,表現可能な最も近い値に丸められる。X及びyが同じ絶対値をもつが反対の符号をもつ
場合,zは正の0とする。x + yが目的の型で表現するには大き過ぎる場合,zは,x + yと同じ符
号をもつ無限大とする。
y
+0
‒0
+∞
‒∞
NaN
x
z
x
x
+∞
‒∞
NaN
+0
y
+0
+0
+∞
‒∞
NaN
‒0
y
+0
‒0
+∞
‒∞
NaN
+∞
+∞
+∞
+∞
+∞
NaN
NaN
‒∞
‒∞
‒∞
‒∞
NaN
‒∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
− 10進実数の加算
decimal operator +(decimal x, decimal y);
結果の値が10進書式で表現するには大き過ぎる場合,例外System.OverflowExceptionが送出
される。丸めを行う前の結果の小数部けた数は,二つ演算対象の小数部けた数のうち大きいほうにな
る。
10進実数の加算は,型System.Decimalの加算演算子を用いたものと同等とする。
− 列挙型の加算。すべての列挙型は,暗黙に,次のあらかじめ定義された演算子を提供する。ここで,
Eは列挙 (enum) 型とし,UはEの基礎とする型とする。
E operator +(E x, U y);
E operator +(U x, E y);
演算子は,正確に(E)((U)x + (U)y)のとおりに評価される。実際の演算対象の一つが型Eのとき,
これらの演算子は,多重定義解決によってだけ考慮される。
− 文字列連結
string operator +(string x, string y);
string operator +(string x, object y);
string operator +(object x, string y);
一つ又は両方の演算対象が型stringの場合の2項演算子+は,文字列の連結を実行する。文字列
の連結の演算対象がnullの場合,空文字列で置き換えられる。そうでない場合,文字列ではないあ
らゆる引数は,型objectから継承される仮想ToStringメソッドを呼び出すことによって,その文
字列表現に変換される。ToStringがnullを返す場合,空文字列で置き換えられる。
例
219
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class Test
{
static void Main() {
string s = null;
Console.WriteLine("s = >" + s + "<"); // displays s = ><
int i = 1;
Console.WriteLine("i = " + i);
// displays i = 1
float f = 1.2300E+15F;
Console.WriteLine("f = " + f);
// displays f =
1.23E+15
decimal d = 2.900m;
Console.WriteLine("d = " + d);
// displays d = 2.900
}
}
文字列連結演算子の結果は,左演算対象の文字(の並び)の後ろに右演算対象の文字(の並び)が
続く文字列とする。文字列連結演算子は,null値を返すことはない。結果の文字列を割り当てるた
めに利用可能な十分なメモリが存在しなかった場合,System.OutOfMemoryExceptionが送出され
る。
− 委譲の組合せ。すべての委譲型は,暗黙に,次のあらかじめ定義された演算子を提供する。ここで,
Dは委譲型とする。
D operator +(D x, D y);
両方の演算対象が委譲型Dの場合の2項演算子+は,委譲の組合せを実行する(演算対象が異なる
委譲型をもつ場合には,多重定義解決では適用可能な演算子を見つけられず,コンパイル時エラーに
なる。)。実際の演算対象の一つが型Dのとき,この演算子は,多重定義解決によってだけ考慮される。
1番目の演算対象がnullの場合,演算の結果は,(たとえ2番目の演算対象もnullであっても,)2
番目の演算対象の値とする。そうでない場合であって,2番目の演算対象がnullの場合,演算の結
果は,1番目の演算対象の値とする。それ以外の場合,演算の結果は,新しい委譲インスタンスにな
り,このインスタンスを呼び出すと,1番目の演算対象が呼び出された後,2番目の演算対象が呼び出
される。つまり,結果である委譲の呼出し並びは,二つの演算対象の呼出し並びの連結となる。
注記 委譲の組合せの例については,14.7.5及び22.3を参照。System.Delegateは委譲型では
ないので,それに対しては,operator +は定義されない。
上で定義したあらかじめ定義された非もち上げの加算演算子のもち上げ形式(14.2.7参照)も,あらか
じめ定義されている。
14.7.5 減算演算子
x ‒ yという形式の演算に対して,演算子実装を選択するために,2項演算子多重定義解決(14.2.4参
照)が適用される。演算対象は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の
返却値の型とする。
あらかじめ定義された減算演算子は,次による。これらの演算子はすべてxからyを引く。
− 整数の減算
220
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int operator ‒(int x, int y);
uint operator ‒(uint x, uint y);
long operator ‒(long x, long y);
ulong operator ‒(ulong x, ulong y);
void operator ‒(long x, ulong y);
void operator ‒(ulong x, long y);
voidを返却する演算子は,常に,コンパイル時エラーとする。したがって,一方の演算対象がlong
で,もう一方の演算対象はulongの場合,エラーとする。
checked文脈において,差が結果の型の値域外の場合,System.OverflowExceptionが送出さ
れる。unchecked文脈では,けた(桁)あふれは報告されず,結果の型の値域外である有意の上位
ビットは破棄される。
− 浮動小数点数の減算
float operator ‒(float x, float y);
double operator ‒(double x, double y);
差は,IEC 60559の算術の規則に従って計算される。次の表は,非0の有限値,0,無限大及びNaN
のすべての可能な組合せの結果を一覧として示す。表では,x及びyは非0の有限値とし,zはx ‒ y
の結果とし,表現可能な最も近い値に丸められる。xとyが等しい場合,zは正の0とする。x ‒ y
が目的の型で表現するには大き過ぎる場合,zは,x ‒ yと同じ符号をもつ無限大とする。
y
+0
‒0
+∞
‒∞
NaN
x
+z
+x
+x
‒∞
+∞
NaN
+0
‒y
+0
+0
‒∞
+∞
NaN
‒0
‒y
‒0
+0
‒∞
+∞
NaN
+∞
+∞
+∞
+∞
NaN
+∞
NaN
‒∞
‒∞
‒∞
‒∞
‒∞
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
− 10進実数の減算
decimal operator ‒(decimal x, decimal y);
結果の値が10進書式で表現するには大き過ぎる場合,例外System.OverflowExceptionが送出
される。丸めを行う前の結果の小数部けた数は,二つの演算対象の小数部けた数のうち大きなほうと
する。
10進実数の減算は,型System.Decimalの減算演算子を用いたものと同等とする。
− 列挙型の減算。すべての列挙型は,次のあらかじめ定義された演算子を提供する。ここで,Eは列挙
型とし,UはEの基礎とする型とする。
U operator ‒(E x, E y);
この演算子は,正確に,(U)((U)x ‒ (U)y)として評価される。すなわち,その演算子は,xとy
の通常の値の差を計算し,結果の型は,列挙型の基礎とする型とする。
E operator ‒(E x, U y);
この演算子は,正確に,(E)((U)x ‒ y)として評価される。すなわち,その演算子は,列挙の基
礎とする型から値を引き,列挙の値を生成する。
221
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 委譲の削除。すべての委譲型は,暗黙に,次のあらかじめ定義された演算子を提供する。ここで,D
は委譲型とする。
D operator ‒(D x, D y);
演算対象が両方とも何らかの委譲型Dに変換可能である場合の2項演算子‒は,委譲の削除を実行
する(演算対象が異なる委譲型をもつ場合には,多重定義解決で適用可能な演算子が見つからず,コ
ンパイル時エラーになる。)。実際の演算対象の一つが型Dのとき,この演算子は,多重定義解決によ
ってだけ考慮される。1番目の演算対象がnullのとき,演算の結果はnullとする。そうでない場
合であって,2番目の演算対象がnullのときは,演算の結果は1番目の演算対象の値とする。そう
でないときには,両方の演算対象は一つ以上の項目をもつ呼出し並び(22.1参照)を表現している。2
番目の演算対象である並びが1番目の演算対象である並びの(項目が)連続する真部分集合となって
いる場合には,結果は,1番目の演算対象である並びから2番目の演算対象である並びの項目を削除
して構成される新しい呼出し並びとする(部分集合の同等性を決定するためには,対応する項目を,
委譲の同等性の演算子(14.9.8参照)によって比較する。)。そうでないときには,結果は,左演算対
象の値とする。演算対象である並びは,いずれも,処理で変化することはない。2番目の演算対象で
ある並びが,1番目の演算対象である並びの中で連続する項目の複数の部分集合と一致する場合,連
続する項目の一致する部分集合のうち最右のものが取り除かれる。削除によって空の並びが生じた場
合,結果はnullとする。
例 次に例を示す。
using System;
delegate void D(int x);
class Test
{
public static void M1(int i) { /* … */ }
public static void M2(int i) { /* … */ }
}
class Demo
{
static void Main() {
D cd1 = new D(Test.M1);
D cd2 = new D(Test.M2);
D cd3 = cd1 + cd2 + cd2 + cd1;
// M1 + M2 + M2 + M1
cd3 -= cd1;
// => M1 + M2 + M2
cd3 = cd1 + cd2 + cd2 + cd1;
// M1 + M2 + M2 + M1
cd3 -= cd1 + cd2;
// => M2 + M1
cd3 = cd1 + cd2 + cd2 + cd1;
// M1 + M2 + M2 + M1
cd3 -= cd2 + cd2;
// => M1 + M1
cd3 = cd1 + cd2 + cd2 + cd1;
// M1 + M2 + M2 + M1
cd3 -= cd2 + cd1;
// => M1 + M2
cd3 = cd1 + cd2 + cd2 + cd1;
// M1 + M2 + M2 + M1
cd3 -= cd1 + cd1;
// => M1 + M2 + M2 + M1
222
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
上で定義したあらかじめ定義された非もち上げの減算演算子のもち上げ形式(14.2.7参照)も,あらか
じめ定義されている。
14.8 シフト演算子
<<及び>>の演算子は,ビットのシフト演算を実行するために用いる。
≪シフト式≫:
≪加減式≫
≪シフト式≫ << ≪加減式≫
≪シフト式≫ ≪右シフト≫ ≪加減式≫
x << count又はx >> countといった形式の演算子に対して,演算子実装を選択するために,2項演
算子多重定義解決(14.2.4参照)が適用される。演算対象は,選択された演算子の仮引数の型に変換され,
その結果の型は,演算子の返却値の型とする。
多重定義されたシフト演算子を宣言する場合,常に,1番目の演算対象の型は演算子の宣言を含むクラ
ス又は構造体とし,2番目の演算対象の型はintにしなければならない。
あらかじめ定義されたシフト演算子は,次による。
− 左シフト
int operator <<(int x, int count);
uint operator <<(uint x, int count);
long operator <<(long x, int count);
ulong operator <<(ulong x, int count);
<<演算子は,xのビットを,次に示すとおりに計算されたビットの数だけ左に移動する。
xの結果の型の値域外の上位ビットは破棄され,残りのビットが左に移動されて,下位の空ビット
位置には0が設定される。
− 右シフト
int operator >>(int x, int count);
uint operator >>(uint x, int count);
long operator >>(long x, int count);
ulong operator >>(ulong x, int count);
>>演算子は,xのビットを,次に示すとおりに計算されたビットの数だけ右に移動する。
xの型がint又はlongの場合,xの下位ビットは破棄され,残りのビットが右に移動されて,上
位の空ビット位置には,xが非負の場合には0が,xが負の場合には1が,設定される。
xの型がuint又はulongの場合,xの下位ビットは破棄され,残りのビットが右に移動されて,
上位の空ビット位置には0が設定される。
あらかじめ定義された演算子について,移動するビットの数は,次のとおりに計算される。
− xの型がint又はuintの場合,移動するビットの数は,countの下位の5ビットによって与えられ
る。すなわち,移動するビットの数はcount & 0x1Fから計算される。
− xの型がlong又はulongの場合,移動するビットの数は,countの下位の6ビットによって与えら
れる。すなわち,移動するビットの数はcount & 0x3Fから計算される。
得られた移動するビットの数が0の場合は,シフト演算子は,単に,xの値を返す。
223
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
シフト演算子は,けた(桁)あふれを引き起こさず,checked文脈とunchecked文脈で同じ結果を生
成する。
演算子>>の左演算対象が符号付き整数型の場合は,算術右シフトが行われ,演算対象の最上位ビット(符
号ビット)の値が上位の空ビット位置に伝ぱ(播)される。演算子>>の左演算対象が符号なし整数型の場
合は,論理右シフトが行われ,上位の空ビット位置には常に0が設定される。符号付き演算対象に論理シ
フト,又は符号なし演算対象に算術シフトを実行するには,明示的なキャストを使用できる。
例 例えば,xが型intの変数の場合,演算unchecked((int)((uint)x >> y))は,xの論理右
シフトを実行する。
上で定義したあらかじめ定義された非もち上げのシフト演算子のもち上げ形式(14.2.7参照)も,あら
かじめ定義されている。
14.9 関係演算子及び型試験演算子
==,!=,<,>,<=,>=,is及びasの演算子は,関係演算子及び型試験演算子という。
≪関係式≫:
≪シフト式≫
≪関係式≫ < ≪シフト式≫
≪関係式≫ > ≪シフト式≫
≪関係式≫ <= ≪シフト式≫
≪関係式≫ >= ≪シフト式≫
≪関係式≫ is ≪型≫
≪関係式≫ as ≪型≫
≪等価式≫:
≪関係式≫
≪等価式≫ == ≪関係式≫
≪等価式≫ != ≪関係式≫
is演算子は14.9.10で,as演算子は14.9.11で規定する。
==,!=,<,>,<=及び>=の演算子は,比較演算子とする。opが比較演算子の場合,x op yという形
式の演算に対して,演算子実装を選択するために,多重定義解決(14.2.4参照)が適用される。演算対象
は,選択された演算子の仮引数の型に変換され,その結果の型は,演算子の返却値の型とする。≪等価式
≫の両方の演算対象がnull型(11.2.7参照)(及びnull値も同様)であれば,多重定義解決は行われず,
その式は,演算子が == 又は!= であるかによってtrue又はfalseの定数値に評価される。
14.9.1〜14.9.11では,あらかじめ定義された比較演算子を規定する。次の表に示すとおり,すべてのあ
らかじめ定義された比較演算子は,bool型の結果を返す。
演算
結果
x == y
xがyに等しい場合にはtrueとし,そうでない場合にはfalseとする。
x != y
xがyに等しくない場合にはtrueとし,そうでない場合にはfalseとする。
x < y
xがyよりも小さい場合にはtrueとし,そうでない場合にはfalseとする。
x > y
xがyよりも大きい場合にはtrueとし,そうでない場合にはfalseとする。
x <= y
xがy以下の場合にはtrueとし,そうでない場合にはfalseとする。
x >= y
xがy以上の場合にはtrueとし,そうでない場合にはfalseとする。
14.9.1 整数比較演算子
224
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
あらかじめ定義された整数比較演算子は,次による。
bool operator ==(int x, int y);
bool operator ==(uint x, uint y);
bool operator ==(long x, long y);
bool operator ==(ulong x, ulong y);
void operator ==(long x, ulong y);
void operator ==(ulong x, long y);
bool operator !=(int x, int y);
bool operator !=(uint x, uint y);
bool operator !=(long x, long y);
bool operator !=(ulong x, ulong y);
void operator !=(long x, ulong y);
void operator !=(ulong x, long y);
bool operator <(int x, int y);
bool operator <(uint x, uint y);
bool operator <(long x, long y);
bool operator <(ulong x, ulong y);
void operator <(long x, ulong y);
void operator <(ulong x, long y);
bool operator >(int x, int y);
bool operator >(uint x, uint y);
bool operator >(long x, long y);
bool operator >(ulong x, ulong y);
void operator >(long x, ulong y);
void operator >(ulong x, long y);
bool operator <=(int x, int y);
bool operator <=(uint x, uint y);
bool operator <=(long x, long y);
bool operator <=(ulong x, ulong y);
void operator <=(long x, ulong y);
void operator <=(ulong x, long y);
bool operator >=(int x, int y);
bool operator >=(uint x, uint y);
bool operator >=(long x, long y);
bool operator >=(ulong x, ulong y);
225
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void operator >=(long x, ulong y);
void operator >=(ulong x, long y);
これらの演算子の各々は,二つの整数である演算対象の数値を比較し,特定の関係がtrue又はfalse
かどうかを示すbool値を返す。返却型がvoid 型の演算子は,常にコンパイル時エラーとする。したが
って,一方の演算対象がlong型で,他方の演算対象がulong型の場合はエラーとする。
上で定義したあらかじめ定義された非もち上げの整数比較演算子のもち上げ形式(14.2.7参照)も,あ
らかじめ定義されている。
14.9.2 浮動小数点数比較演算子
あらかじめ定義された浮動小数点数比較演算子は,次による。
bool operator ==(float x, float y);
bool operator ==(double x, double y);
bool operator !=(float x, float y);
bool operator !=(double x, double y);
bool operator <(float x, float y);
bool operator <(double x, double y);
bool operator >(float x, float y);
bool operator >(double x, double y);
bool operator <=(float x, float y);
bool operator <=(double x, double y);
bool operator >=(float x, float y);
bool operator >=(double x, double y);
これらの演算子は,IEC 60559の算術の規則に従って演算対象を比較する。
演算対象のいずれかがNaNの場合,!=以外のすべての演算子に対して,結果はfalseとする。!=に対
しては,結果はtrueとする。任意の二つの演算対象に対して,x != yは,常に!(x == y)と同じ結果
を生成する。しかし,一つ又は両方の演算対象がNaNの場合,<,>,<=及び>=の演算子は,それと反対
の演算子の論理否定と同じ結果を生成するわけではない。
例 例えば,x及びyのいずれかがNaNの場合,x < yはfalseとなるが,!(x >= y)はtrueと
なる。
− 演算対象の双方ともがNaNでない場合,演算子は,二つの浮動小数点数演算対象の値を次の大小順序
で比較する。
‒∞ < ‒max < … < ‒min < ‒0.0 == +0.0 < +min < … < +max < +∞
ここで,min及びmaxは,与えられた浮動小数点数形式で表現可能な最小及び最大の正の有限値と
する。この大小順序では,次の点に注意する。
・ 負の0と正の0は,等しいとみなされる。
・ 負の無限大は,他のすべての値より小さいとみなされるが,他の負の無限大とは等しいとみなされ
226
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る。
・ 正の無限大は,他のすべての値より大きいとみなされるが,他の正の無限大とは等しいとみなされ
る。
上で定義したあらかじめ定義された非もち上げの浮動小数点数比較演算子のもち上げ(14.2.7参照)形
式も,あらかじめ定義されている。
14.9.3 10進実数比較演算子
あらかじめ定義された10進実数比較演算子は,次による。
bool operator ==(decimal x, decimal y);
bool operator !=(decimal x, decimal y);
bool operator <(decimal x, decimal y);
bool operator >(decimal x, decimal y);
bool operator <=(decimal x, decimal y);
bool operator >=(decimal x, decimal y);
これらの演算子の各々は,二つの10進実数の演算対象の数値を比較し,特定の関係がtrue又はfalse
のいずれかとなることを示すbool値を返す。各々の10進実数の比較は,型System.Decimalの対応す
る関係演算子又は等価演算子を用いたものと同等とする。
上で定義したあらかじめ定義された非もち上げの10進実数比較演算子のもち上げ(14.2.7参照)形式も,
あらかじめ定義されている。
14.9.4 論理型等価演算子
あらかじめ定義された論理型等価演算子は,次による。
bool operator ==(bool x, bool y);
bool operator !=(bool x, bool y);
==の結果は,x及びyの両方がtrueの場合,又は,x及びyの両方がfalseの場合,trueとする。
それ以外の場合は,falseとする。
!=の結果は,x及びyの両方がtrueの場合,又はx及びyの両方がfalseの場合,falseとする。
それ以外の場合は,trueとする。演算対象の型がboolの場合,!=演算子は,^演算子と同じ結果を生成
する。
上で定義したあらかじめ定義された非もち上げの論理型等価演算子のもち上げ(14.2.7参照)形式も,
あらかじめ定義されている。
14.9.5 列挙型比較演算子
すべての列挙型は,暗黙に,次のあらかじめ定義された比較演算子を提供する。
bool operator ==(E x, E y);
bool operator !=(E x, E y);
bool operator <(E x, E y);
bool operator >(E x, E y);
bool operator <=(E x, E y);
bool operator >=(E x, E y);
x op yを評価した結果は,((U)x) op ((U)y)を評価したものと正確に同じとする。ここで,x及びy
227
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
は,基礎とする型がUである列挙型Eの式,そしてopは,比較演算子の一つとする。すなわち,列挙型
比較演算子は,単に,二つの演算対象の基礎とする整数値を比較する。実際の演算対象の一つが型Eであ
るとき,これらの演算子は,多重定義解決(14.2.4参照)によってだけ考慮される。
上で定義したあらかじめ定義された非もち上げの列挙型比較演算子のもち上げ(14.2.7参照)形式も,
あらかじめ定義されている。
14.9.6 参照型等価演算子
どのクラス型Cに対しても,別のあらかじめ定義された等価演算子が存在しなければ(例えば,Cが
string 又はSystem.Delegateのとき),次のあらかじめ定義された参照型等価演算子が暗黙に提供さ
れる。
bool operator ==(C x, C y);
bool operator !=(C x, C y);
これらの演算子は,等価性又は非等価性について二つの参照を比較した結果を返す。
参照型等価演算子が適用可能な場合を決定するための特別な規則がある。型Aと型Bとを演算対象とす
る≪等価式≫に対して,A0を次のように定義する。
− Aが参照型(25.7参照)であることが分かっている型仮引数であれば,A0をAの実効基底クラスにす
る。
− そうではない場合であって,Aがインタフェース型,委譲型,配列型,クラス型又はnull型(11.2.7
参照)であれば,A0はAと同じにする。
次にA1を次のように定義する。
− A0がインタフェース型,委譲型,System.Delegate又はstringであれば,A1をobjectとする。
− そうではない場合であって,A0が配列型であれば,A1をSystem.Arrayとする。
− そうではなければ,A0はnull型又はクラス型であり,A1をA0と同じ型とする
B0及びB1を,同様に定義する。この状況では,次のように任意の参照型等価演算子が適用可能かどう
かを決定する。
− 型A及び型Bがnull型であれば,多重定義解決が実行されず,その結果は14.9で示したとおり,定
数true又はfalseである。
− そうではない場合であって,Aがnull型であれば,B1に対する参照型等価演算子を適用できる。B
が型仮引数であり,及び実行時の型仮引数がnull許容でない値型であれば,その演算の結果はfalse
である。Bが型仮引数であり,及び実行時の型仮引数がnull許容型又は参照型であれば,その結果
は型B の演算対象がボックス化され,nullと比較される。
− そうではない場合であって,Bがnull型であれば,A1に対する参照型等価演算子を適用できる。A
が型仮引数であり,及び実行時の型仮引数がnull許容でない値型であれば,その演算の結果はfalse
である。Aが型仮引数であり,及び実行時の型仮引数がnull許容型又は参照型であれば,その結果
は型Aの演算対象がボックス化され,nullと比較される。
− そうではない場合であって,型A又は型Bが参照型(25.7を参照)でないことが知られている型仮引
数であれば,参照型等価演算子は適用されない。
− そうではない場合であって,A0からB0への恒等変換若しくは(暗黙的な又は明示的な)参照変換,
又はB0からA0への恒等変換若しくは(暗黙的な又は明示的な)参照変換がなければ,参照型等価演
算子は適用できない。
− そうではない場合であって,A1からB1への恒等変換又は暗黙的な参照変換があれば,B1に対する参
228
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
照型等価演算子を適用できる。
− そうではない場合であって,B1からA1への暗黙的な参照変換があれば,A1に対する参照型等価演算
子を適用できる。
− そうでなければ,参照型等価演算子は適用されない。
注記 これらの規則が暗黙に示す注意点は,次のとおり。
− あらかじめ定義された参照型等価演算子は,コンパイル時に異なることが分かっている二つの参
照を比較するために利用することはできない。
例 例えば,演算対象のコンパイル時の型が二つのクラスA及びBであって,AもBももう一方
から派生しない場合には,それらの二つの演算対象が同じオブジェクトを参照することは不
可能であり,参照型等価演算子を適用できない。同様に,Aが封印クラスであり,BがAで
実装していないインタフェースである場合も,参照型等価演算子は適用できない。
− あらかじめ定義された参照型等価演算子は,値型の演算対象を比較することを許さない。
− あらかじめ定義された参照型等価演算子は,その演算対象にボックス化演算を発生させない。新
しく割り当てられたボックス化されたインスタンスへの参照は,必然的に他のすべての参照と異
なっているので,そのようなボックス化演算を実行しても意味がないからである。
演算子多重定義解決(14.2.4参照)の規則が,あらかじめ定義された参照型等価演算子の代わりに等価
演算子を選択する場合でも,演算対象の一方又は両方をobject型に明示的にキャストすることによって,
あらかじめ定義された参照型等価演算子を選択できる。
例 次に例を示す。
using System;
class Test
{
static void Main() {
string s = "Test";
string t = string.Copy(s);
Console.WriteLine(s == t);
Console.WriteLine((object)s == t);
Console.WriteLine(s == (object)t);
Console.WriteLine((object)s == (object)t);
}
}
これは,次を出力する。
True
False
False
False
変数s及びtは,同じ文字を含む二つの異なるstringインスタンスを参照する。最初の比較
は,両方の演算対象の型がstringの場合であって,あらかじめ定義された文字列等価演算子
(14.9.7参照)が選択されるので,Trueを出力する。残りの比較はすべて,演算対象の一方又は
両方の型がobjectの場合であって,あらかじめ定義された参照型等価演算子が選択されるので,
229
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Falseを出力する。
この技法は,値型では意味がないことに注意する。次に例を示す。
class Test
{
static void Main() {
int i = 123;
int j = 123;
System.Console.WriteLine((object)i == (object)j);
}
}
これは,キャストが二つの別々のボックス化されたint値のインスタンスへの参照を生成する
ので,Falseを出力する。
14.9.7 文字列等価演算子
あらかじめ定義された文字列等価演算子は,次による。
bool operator ==(string x, string y);
bool operator !=(string x, string y);
二つのstring値は,次のうちの一つが真の場合,等しいとみなす。
− 両方の値がnullである。
− 両方の値が,同一の長さであり,かつ,各々の文字の位置に同一の文字をもつ文字列インスタンスへ
の非nullな参照である。
文字列等価演算子は,文字列の参照ではなく文字列の値を比較する。二つの別々の文字列インスタンス
が正確に同じ文字列の列を含む場合,文字列の値は等しいが,参照は異なる。
注記 14.9.6で示されるとおり,参照型等価演算子を用いて,文字列の値の代わりに文字列の参照を
比較できる。
14.9.8 委譲等価演算子
すべての委譲型は,暗黙に,次のあらかじめ定義された比較演算子を提供する。ここで,Dは委譲型と
する。
bool operator ==(D x, D y);
bool operator !=(D x, D y);
実際の演算対象の一つが型Dであるとき,これらの演算子は,多重定義解決(14.2.4参照)によってだ
け考慮される。
コンパイル時に委譲型であることが分からない場合,次のあらかじめ定義された演算子が委譲型を比較
するために利用できる。
bool operator ==(System.Delegate x, System.Delegate y);
bool operator !=(System.Delegate x, System.Delegate y);
演算対象の型が委譲型でなければ,これらの後者の演算子は,適用可能とはみなされない。
例 次に例を示す。
delegate void D();
delegate void E();
static bool Compare1(D d, E e) {
230
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
return d == e; // Error
}
static bool Compare2(System.Delegate d, E e) {
return d == e; // OK
}
Compare1内の比較は,演算対象の型が委譲型とは異なるので,エラーとなる。この場合,委
譲等価演算子も参照等価演算子も適用可能ではない。DのnullでないインスタンスはEのイン
スタンスとは等価ではないからである。
二つの委譲インスタンスは,次の場合に等しいとみなされる。
− 委譲インスタンスの一方がnullの場合,両方がnullの場合であって,かつ,その場合に限り,そ
れらは等しい。
− 委譲インスタンスが異なる実行時型をもつ場合,それらは等しくない。
− 委譲インスタンスの両方が一つの呼出し並び(22.1参照)をもつ場合,呼出し並びの長さが等しく,
一方の呼出し並びの個々の入口がもう一方の呼出し並びで対応する入口に順序どおり等しい場合であ
って(次に定義する),かつ,その場合に限り,それらは等しい。
次の規則が,呼出し並びの要素の等価性を決定する。
・ 二つの呼出し並びの入口の両方が,同じ静的メソッドを参照している場合は,それらの入口は等し
い。
・ 二つの呼出し並びの入口の両方が,(参照等価演算子での定義で)同じ対象オブジェクト上の同じ非
静的メソッドを参照している場合は,それらの入口は等しい。
− 捕そく(捉)された外変数インスタンスの同一の集合(空かもしれない)と意味的に一致する≪無名
メソッド式≫を評価した結果として生成される呼出し並びは,等価とすることができる(要求はされ
ない)。
− 意味的に異なる≪無名メソッド式≫の評価した結果として生成される呼出し並び,又は捕そく(捉)
された外変数インスタンスの異なる集合をもつ呼出し並びは等しくならない。
− ≪無名メソッド式≫を評価した結果として生成される一つの呼出し並びは,メソッド群に対する≪委
譲生成式≫から生成される呼出し並びとは等しくならない。
14.9.9 等価演算子及びnull許容型
演算子==及び演算子!=は,一方の演算対象がnull許容型の値をもち,他方の演算対象がnull型(11.2.7
参照)をもつことを許可する。その操作に対して,あらかじめ定義された演算子又は利用者定義の演算子
(非もち上げ形式又はもち上げ形式)が存在していなくてもよい。
次の四つの形式のいずれかをもつ操作に対して,演算子多重定義解決(14.2.4参照)で適用可能な演算
子を見つけられなかった場合,その結果は,xの特性hasValueから計算される。
x == null
null == x
x != null
null != x
ここで,xはnull許容型の式とする。具体的には,最初の二つの形式は!x.hasValueに翻訳され,
後半の二つの形式はx.hasValueに翻訳される。
14.9.10
is演算子
231
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
is演算子は,オブジェクトの実行時型と与えられた型とが互換かどうかを動的に検査するために用いる。
eを式,かつ,Tを型とする場合に,操作e is Tの結果は,参照変換,ボックス化変換,ボックス化解除
変換,包装変換及び包装解除変換によって,eがTに正しく変換できるかどうかを示す真理値とする。演
算は,次のとおりに評価される。
− eのコンパイル時の型がTと同じ場合,又は,暗黙の参照変換(13.1.4参照),ボックス化変換(13.1.5
参照)若しくは包装変換(13.7参照)がeのコンパイル時の型からTへの変換として存在する場合。
・ eの型が参照型の場合,演算の結果は,e != nullを評価したものに同等とする。
・ eの型がnull許容型の場合,演算の結果は,e != nullを評価したものに同等とする。
・ eの型が値型の場合は,演算の結果は,trueとする。
− そうでない場合であって,eのコンパイル時の型からTへの包装解除変換(13.7参照)が存在する場
合,演算の結果は,e != nullを評価したものに同等とする。
− そうでない場合であって,eのコンパイル時の型からTへの明示的な参照変換(13.2.3参照)又はボ
ックス化解除変換(13.2.4参照)が存在する場合,動的な型検査が次のとおりに実行される。
・ eの値がnullの場合,結果はfalseとする。
・ そうでない場合であって,Tがnull許容型であり,eの実行時型がTの基礎とする型の場合,結
果はtrueとする。
・ そうでない場合であって,Rをeが参照するインスタンスの実行時の型としたとき,RとTが同じ
型である場合,Rが参照型であってRからTへの暗黙の参照変換が存在する場合,又はRが値型で
あってTがRによって実装されるインタフェース型である場合,結果はtrueとする。
・ そうでなければ,結果はfalseとする。
− そうでない場合であって,eのコンパイル時の型又は型Tが開型(25.5.2参照)であれば,動的な型
検査が実行される。
− そうでなければ,eの型Tへの参照変換,ボックス化変換,包装変換,又は包装解除変換は可能では
なく,演算の結果はfalseとする。
is演算子は,参照変換,ボックス化変換及びボックス化解除変換だけを考慮する。利用者定義の変換の
ような他の変換は,is演算子によって考慮されない。
14.9.11
as演算子
as演算子は,値を明示的に与えられた参照型又はnull許容型に変換するために用いる。参照型に変換
するときは,as演算子は参照変換又はボックス化変換を用いる。null許容型に変換するときは,as演
算子は包装変換,ボックス化解除変換及びnull型変換(13.7.1参照)を用いる。キャスト式(14.6.6参照)
とは異なり,as演算子は,例外を送出することはない。その代わりに,指示された変換が可能ではない場
合,結果の値はnullになる。
e as Tという形式の操作では,eは式でなければならず,Tは参照型,参照型(25.7参照)であること
が分かっている型仮引数,又はnull許容型でなければならない。結果の型はTであって,結果は常に値
として分類される。演算は,次のとおりに評価される。
− eのコンパイル時の型がTと同じ場合,結果は,単にeの値とする。
− そうでない場合であって,eのコンパイル時の型からTへの暗黙の参照変換(13.1.4参照),ボックス
化変換(13.1.5参照),包装変換(13.7参照),又はnull型変換(13.7.1参照)が存在する場合,この
変換が実行され,それが演算の結果になる。
− そうでない場合であって,eのコンパイル時の型からTへの明示的な参照変換(13.2.3参照),又はボ
232
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ックス化解除変換(13.2.4参照)が存在する場合,動的な型検査が次のとおりに実行される。
・ eの値がnullの場合,結果は,コンパイル時の型Tをもつ値nullとする。
・ そうでない場合であって,Tがnull許容型である場合,Rをeが参照するインスタンスの実行時
の型としたとき,RがTの基礎となる型であれば,結果は,eによって与えられた値を型Tにボッ
クス化解除したのと同等である。
・ そうでない場合であって,Rをeが参照するインスタンスの実行時の型としたとき,R及びTが同
じ型であってRが参照型であってRからTへの暗黙の参照変換が存在する場合,又はRが値型であ
ってTがRによって実装されるインタフェース型の場合,結果は,コンパイル時の型Tをもつeに
よって与えられる参照とする。
・ そうでなければ,結果は,コンパイル時の型Tをもつ値nullとする。
− そうでない場合であって,eのコンパイル時の型又は型Tが開型(25.5.2参照)であれば,動的な型
検査が実行される。
− そうでなければ,指示された変換は実行できず,コンパイル時エラーになる。
as演算子は,参照変換,ボックス化変換,ボックス化解除変換又は包装解除変換だけを実行する。利用者
定義の変換などの他の変換は,as演算子では可能ではない。その代わりにキャスト式を用いて実行するこ
とを推奨する。
as演算子は,Tが参照型(25.7参照)と分かっている場合に限り,右辺に型仮引数T(25.1.1参照)を使
ってもよい。この制約は,値nullが,この演算子の結果とした返すことができるために必要である。
例 次に例を示す。
class X
{
public T F<T>(object o) where T: Attribute {
return o as T; // OK, T has a class type constraint
}
public T G<T>(object o) {
return o as T; // Error, unconstrainted T
}
注記 制約がある場合,操作 e as T が決して成功しないとコンパイラが結論付けられる状況があ
る。例えば,eが特定のインタフェースを実装していない構造体又は封印クラスであり,Tが
インタフェースを要求するように制約を掛けられている場合である。しかし,この状況に対す
る完全な仕様は複雑であり,上で示した規則が最もよい妥協案と思われる。
14.10
論理演算子
&,^及び|の演算子を,論理演算子という。
≪論理積式≫:
≪等価式≫
≪論理積式≫ & ≪等価式≫
≪排他的論理和式≫:
≪論理積式≫
≪排他的論理和式≫ ^ ≪論理積式≫
≪論理和式≫:
233
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪排他的論理和式≫
≪論理和式≫ | ≪排他的論理和式≫
opが論理演算子の一つであるx op yという形式の演算に対して,特定の演算子実装を選択するために,
多重定義解決(14.2.4参照)が適用される。演算対象は,選択された演算子の仮引数の型に変換され,そ
の結果の型は,演算子の返却値の型になる。
14.10.1〜14.10.4では,あらかじめ定義された論理演算子を示す。
14.10.1
整数論理演算子
あらかじめ定義された整数論理演算子は,次による。
int operator &(int x, int y);
uint operator &(uint x, uint y);
long operator &(long x, long y);
ulong operator &(ulong x, ulong y);
void operator &(long x, ulong y);
void operator &(ulong x, long y);
int operator |(int x, int y);
uint operator |(uint x, uint y);
long operator |(long x, long y);
ulong operator |(ulong x, ulong y);
void operator |(long x, ulong y);
void operator |(ulong x, long y);
int operator ^(int x, int y);
uint operator ^(uint x, uint y);
long operator ^(long x, long y);
ulong operator ^(ulong x, ulong y);
void operator ^(long x, ulong y);
void operator ^(ulong x, long y);
&演算子は二つの演算対象のビット単位の論理積 (AND) を計算し,|演算子は二つの演算対象のビット
単位の論理和 (OR) を計算し,^演算子は二つの演算対象のビット単位の排他的論理和 (exclusive OR) を計
算する。これらの演算から,けた(桁)あふれは生じない。返却型がvoid 型の演算子は,常にコンパイ
ル時エラーとする。したがって,一方の演算対象がlong型で,他方の演算対象がulong型の場合はエラ
ーとする。
上で定義したあらかじめ定義された非もち上げの整数論理演算子のもち上げ(14.2.7参照)形式も,あ
らかじめ定義されている。
14.10.2
列挙論理演算子
すべての列挙型Eは,暗黙に,次のあらかじめ定義された論理演算子を提供する。
E operator &(E x, E y);
E operator |(E x, E y);
E operator ^(E x, E y);
234
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
x op yを評価した結果は,(E)((U)x op (U)y)を評価したものと正確に同じとする。ここで,x及びy
は基礎とする型Uをもつ列挙型Eの式とし,opは論理演算子の一つとする。すなわち,列挙型の論理演算
子は,単に,二つの演算対象の基礎とする型についての論理演算を実行する。実際の演算対象の一つが型
Eであるとき,これらの演算子は,多重定義解決(14.2.4参照)によってだけ考慮される。
上で定義したあらかじめ定義された非もち上げの列挙論理演算子のもち上げ(14.2.7参照)形式も,あ
らかじめ定義されている。
14.10.3
真理値論理演算子
あらかじめ定義された真理値論理演算子は,次による。
bool operator &(bool x, bool y);
bool operator |(bool x, bool y);
bool operator ^(bool x, bool y);
x & yの結果は,x及びyの両方がtrueの場合,trueとする。それ以外の場合は,falseとする。
x | yの結果は,x又はyがtrueの場合,trueとする。それ以外の場合は,falseとする。
x ^ yの結果は,xがtrueであってyがfalseの場合,又はxがfalseであってyがtrueの場合
に,trueとする。それ以外の場合は,falseとする。演算対象の型がboolの場合,^演算子は,!=演
算子と同じ結果を計算する。
14.10.4
bool?論理演算子
null許容真理値型bool?は,true,false 及びnullの三つの値を表現できる。これは,SQLの真
理値式で利用される3値型を概念的に同等である。bool?を演算対象とする&演算子及び|演算子によって
生成される結果がSQLの3値論理に矛盾しないことを保証するために,次のあらかじめ定義された演算子
を提供する。
bool? operator &(bool? x, bool? y);
bool? operator |(bool? x, bool? y);
次の表に,true,false及びnullのすべての組合せについて,これらの演算子によって生成される
結果を示す。
x
y
x&y
x|y
true
true
True
true
true
false
False
true
true
null
null
true
false
true
false
true
false
false
false
false
false
null
false
null
null
true
null
true
null
false
false
null
null
null
null
null
14.11
二択条件論理演算子
&&演算子及び||演算子を,二択条件論理演算子という。また,“短絡”論理演算子ともいう。
≪二択条件論理積式≫:
≪論理和式≫
≪二択条件論理積式≫ && ≪論理和式≫
≪二択条件論理和式≫:
235
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪二択条件論理積式≫
≪二択条件論理和式≫ || ≪二択条件論理積式≫
&&演算子及び||演算子は,&演算子及び|演算子に条件が付いたものとする。
− 演算x && yは,演算x & yに対応するが,xがtrueの場合だけにおいてyが評価される。
− 演算x || yは,演算x | yに対応するが,xがfalseの場合だけにおいてyが評価される。
x && y又はx || yという形式の演算は,その演算がx & y又はx | yと書かれているものとして,
多重定義解決(14.2.4参照)を適用して処理される。このとき,次がいえる。
− 多重定義解決によって一つの最適な演算子を見つけられない場合,又は多重定義解決によってあらか
じめ定義された整数論理演算子の一つが選択された場合は,コンパイル時エラーになる。
− そうでない場合であって,選択された演算子があらかじめ定義された真理値論理演算子(14.10.2参照)
の一つである場合,演算は,14.11.1で示されるとおりに処理される。
− そうでない場合,選択された演算子は利用者定義の演算子であって,その演算は14.11.2で示されると
おりに処理される。
二択条件論理演算子を直接に多重定義することはできない。しかし,二択条件論理演算子は通常の論理
演算子によって評価されるので,若干の制限はあるが,通常の論理演算子の多重定義は,二択条件論理演
算子の多重定義ともみなされる。これは,14.11.2で更に規定される。
14.11.1
真理値二択条件論理演算子
x&y又はx|yに対する多重定義解決(14.2.4参照)が,あらかじめ定義された真理値の&演算子又は|演
算子を選択する場合,x&&y又はx||yは,次のとおりに処理される。
− 演算x && yは,(bool)x ? (bool) y : falseとして評価される。すなわち,xが最初に評価さ
れ,型boolに変換される。そのとき,xがtrueの場合,yが評価され,型boolに変換されて,こ
れが演算の結果になる。それ以外の場合には,演算の結果はfalseとする。
− 演算x || yは,(bool)x ? true : (bool)yとして評価される。すなわち,xが最初に評価され,
型boolに変換される。そのとき,xがtrueの場合,演算の結果はtrueとする。それ以外の場合
には,yが評価され,型boolに変換されて,これが演算の結果になる。
14.11.2
利用者定義の二択条件論理演算子
x&y又はx|yに対する多重定義解決(14.2.4参照)が,利用者定義の&演算子又は|演算子を選択する場
合,x&&y又はx||yの処理は次の両方が真であることを要求する。ここで,Tは,選択された演算子が宣
言されている型とする。
− 選択された演算子の返却型及び各々の仮引数の型は,Tでなければならない。すなわち,演算子は,
型Tの二つの演算対象の論理積又は論理和を計算しなければならず,型Tの結果を返却しなければな
らない。
− Tは,operator true及びoperator falseの宣言を含まなければならない。
これらの要件のいずれかが満たされない場合,コンパイル時エラーになる。これらの要件がすべて満た
された場合,&&又は||の演算は,選択された利用者定義の演算子と,利用者定義のoperator true又
はoperator falseとを結合することによって評価される。
− 演算x && yは,T.false((T)x) ? (T)x : T.&((T)x, y)として評価される。ここで,
T.false((T)x)はTで宣言されたoperator falseの呼出しとし,T.&((T)x, y)は選択された
operator &の呼出しとする。さらに,値(T)xの評価は一度だけでなければならない。すなわち,x
が最初に評価され,型Tに変換され,xが確定的に偽 (false) かどうかを決定するために,その結
236
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
果についてoperator falseが呼び出される。そのとき,xが確定的に偽の場合,演算の結果は,x
に対して先に型Tに変換された値とする。それ以外の場合には,yが評価され,選択された
operator &が,その演算の結果を生成するために,xに対して先に型Tに変換された値及びyに対
して計算された値について呼び出される。
− 演算x || yは,T.true((T)x) ? (T)x : T.|((T)x, y)として評価される。ここで,
T.true((T)x)は,Tで宣言されたoperator trueの呼出しとし,T.|((T)x, y)は,選択された
operator |の呼出しとする。さらに,値(T)xの評価は一度だけでなければならない。すなわち,x
が最初に評価され,型Tに変換され,xが確定的に真 (true) かどうかを決定するために,その結果
についてoperator trueが呼び出される。そのとき,xが確定的に真の場合,演算の結果は,xに
対して先に型Tに変換された値とする。それ以外の場合には,yが評価され,選択されたoperator |
が,その演算の結果を生成するために,xに対して先に型Tに変換された値及びyに対して計算され
た値について呼び出される。
これらの演算のいずれも,xによって与えられる式は,一度だけ評価され,yによって与えられた式は,
評価されないか又は正確に一度だけ評価されるかのいずれかとする。
14.12
null判定選択演算子
??演算子をnull判定選択演算子という。
≪null判定選択式≫:
≪二択条件論理和式≫
≪二択条件論理和式≫ ?? ≪null判定選択式≫
a ?? bという形式のnull判定選択式は,aがnull許容型又は参照型であることを要求する。aが
nullでなければ,a ?? bの結果はaとする。aがnullであれば,結果はbとする。aがnullである
ときだけ,bが評価される。
null判定選択演算子の結合規則は,右結合とする。すなわち,演算は右から左へとまとめられる。
例 a ?? b ?? cという形式の式は,a ?? (b ?? c) として評価される。一般的に,E1 ?? E2 ??
… ?? ENという形式の式は,nullでない最初の演算対象を返却するか,又はすべての演算対象
がnullの場合はnullを返却する。
a ?? bの式の型は,演算対象の型の間での暗黙的な変換が利用可能かどうかに依存する。望ましい順
に,a ?? bの型は,A0,A又はBとする。ここで,Aはaの型,Bはbの型,A0はAから(もしあれば)?
修飾子を削除した型である。厳密にはa ?? bは,次のように処理される。
− Aがnull許容型又は参照型でなければ,コンパイル時エラーとする。
− Aがnull許容型であり,bからA0への暗黙の変換が存在すれば,結果の型はA0になる。実行時に,
aが最初に評価される。aがnullでなければ,aは型A0に包装解除され,それが結果になる。そう
でなければ,bが評価され,A0に変換され,それが結果になる。
− そうでない場合であって,bからAへの暗黙の変換が存在すれば,結果の型はAになる。実行時に,
aが最初に評価される。aがnullでなければ,aが結果になる。そうでなければ,bが評価され,型
Aに変換され,それが結果になる。
− そうでない場合であって,A0からBへの暗黙の変換が存在すれば,結果の型はBになる。実行時に,
aが最初に評価される。aがnullでなければ,(AとA0とが同じ型でない限り)aが型A0に包装解
除され,それがBに変換され,結果になる。そうでなければ,bが評価され,それが結果になる。
そうでなければ,aとbとは非互換であり,コンパイル時エラーとする。
237
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
14.13
二択条件演算子
?:演算子を,二択条件演算子という。3項演算子ということもある。
≪二択条件式≫:
≪null判定選択式≫
≪null判定選択式≫ ? ≪式≫ : ≪式≫
b ? x : yという形式の二択条件式は,最初に条件bを評価する。それから,bがtrueの場合,xが
評価され,それが演算の結果になる。それ以外の場合,yが評価され,それが演算の結果になる。二択条
件式は,x及びyの両方を評価することはない。
二択条件演算子の結合規則は,右結合とする。すなわち,演算は右から左へとまとめられる。
例 例えば,a ? b : c ? d : eという形式の式は,a ? b : (c ? d : e)として評価される。
?:演算子の1番目の演算対象は,暗黙にboolに変換できる型の式,又はoperator trueを実装する
型の式でなければならない。これらの要件のいずれも満たしていない場合,コンパイル時エラーになる。
注記 型bool?は,自明な方法でoperator true及びoperator falseを実装しているものとみ
なされる。型bool?の式は,?:式の1番目の演算対象として利用できる。
?:演算子の2番目及び3番目の演算対象は,二択条件式の型を制御する。X及びYを2番目及び3番目
の演算対象としたとき,二択条件式の型は,次による。
− XとYが同じ型の場合,これを二択条件式の型とする。
− そうでない場合であって,XからYへの暗黙変換(13.1参照)が存在するが,YからXへの暗黙変換
が存在しない場合,Yを二択条件式の型とする。
− そうでない場合であって,YからXへの暗黙変換(13.1参照)が存在するが,XからYへの暗黙変換
が存在しない場合,Xを二択条件式の型とする。
− そうでなければ,式の型は決定できず,コンパイル時エラーになる。
b ? x : yという形式の二択条件式の実行時処理は,次の段階で構成される。
− 最初に,bが評価され,bのbool値が決定される。
・ bの型からboolへの暗黙変換が存在する場合,この暗黙変換が,bool値を生成するために実行さ
れる。
・ 存在しない場合は,bの型によって定義されるoperator trueが,bool値を生成するために呼
び出される。
− 前項の段階で生成されたbool値がtrueの場合,xが評価され二択条件式の型に変換されて,これ
が二択条件式の結果になる。
− そうでなければ,yが評価され二択条件式の型に変換されて,これが二択条件式の結果になる。
14.14
代入演算子
代入演算子は,新しい値を変数,特性,イベント又は添字子の要素に代入する。
≪代入≫:
≪単項式≫ ≪代入演算子≫ ≪式≫
≪代入演算子≫: 次のいずれか
= += -= *= /= %= &= |= ^= <<= ≪右シフト代入≫
代入の左演算対象は,変数,特性アクセス,添字子アクセス又はイベントアクセスに分類される式でな
ければならない。
=演算子は,単純代入演算子と呼ばれる。それは,右演算対象の値を左演算対象によって与えられる変
238
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
数,特性,又は添字子の要素に代入する。単純代入演算子の左演算対象は,17.7.1で規定された以外は,
イベントアクセスであってはならない。単純代入演算子は,14.14.1で規定される。
2項演算子を文字=に先行させることによって形成される演算子は,複合代入演算子という。これらの演
算子は,二つの演算対象について指示された演算を実行し,左演算対象によって与えられた変数,特性又
は添字子の要素に結果値を代入する。複合代入演算子は,14.14.2で規定される。
左演算対象としてイベントアクセス式をもつ+=演算子及び-=演算子は,イベント代入演算子という。そ
れ以外の代入演算子が左演算対象としてイベントアクセスをもつことは,有効ではない。イベント代入演
算子は,14.14.3で規定される。
代入演算子の結合規則は,右結合とする。すなわち,演算は,右から左へとまとめられる。
例 例えば,a = b = cという形式の式は,a = (b = c)として評価される。
14.14.1
単純代入
=演算子は,単純代入演算子という。単純代入では,右演算対象は,左演算対象の型に暗黙に変換可能
な式でなければならない。演算は,右演算対象の値を左演算対象によって与えられる変数,特性又は添字
子の要素に代入する。
単純代入式の結果は,左演算対象に代入される値とする。結果は,左演算対象と同じ型をもち,常に値
として分類される。
左演算対象が特性アクセス又は添字子アクセスである場合,その特性又は添字子は,setアクセス子を
もたなければならない。そうでない場合は,コンパイル時エラーになる。
x = yという形式の単純代入の実行時処理は,次の段階で構成される。
− xが変数として分類される場合
・ xが,その変数を生成するために評価される。
・ yが評価され,必要ならば,暗黙変換(13.1参照)によってxの型に変換される。
・ xによって与えられる変数が≪参照型≫の配列要素の場合,yで計算される値がxが要素となる配
列インスタンスと互換になることを保証するために,実行時検査が実行される。検査は,yがnull
の場合,又はyが参照するインスタンスの実際の型からxを含む配列インスタンスの実際の要素の
型への暗黙の参照変換(13.1.4参照)が存在する場合,成功する。それ以外の場合は,
System.ArrayTypeMismatchExceptionが送出される。
・ yの評価及び変換から生じる値は,xの評価によって与えられる位置に格納される。
− xが特性アクセス又は添字子アクセスとして分類される場合
・ xと関連するインスタンス式(xがstaticでない場合)及び実引数並び(xが添字子アクセスの
場合)が評価され,その結果がそれに続くsetアクセス子の呼出しで使用される。
・ yが評価され,必要ならば,暗黙変換(13.1参照)によってxの型に変換される。
・ yで計算された値をxのsetアクセス子の実引数value として,xのsetアクセス子が呼び出さ
れる。
注記 配列共変規則(19.5参照)は,BからAへの暗黙の参照変換が存在するという条件の下では,
配列型A[]の値を配列型B[]のインスタンスへの参照とすることを許可する。これらの規則に
よって,≪参照型≫の配列要素への代入は,代入されている値が配列のインスタンスと互換に
なることを保証するために,実行時検査を要求する。次に例を示す。
string[] sa = new string[10];
object[] oa = sa;
239
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
oa[0] = null;
// Ok
oa[1] = "Hello";
// Ok
oa[2] = new ArrayList();
// ArrayTypeMismatchException
この最後の代入は,ArrayListのインスタンスがstring[]の要素には格納できないので,
System.ArrayTypeMismatchExceptionを送出する。
≪構造体型≫で宣言される特性又は添字子が代入の対象になる場合,特性アクセス又は添字子アクセス
と関連付けられるインスタンス式は,変数として分類されなければならない。インスタンス式が値に分類
される場合は,コンパイル時エラーになる。
注記 14.5.4によって,同じ規則がフィールドにも適用される。
例 次の宣言が与えられたとする。
struct Point
{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int X {
get { return x; }
set { x = value; }
}
public int Y {
get { return y; }
set { y = value; }
}
}
struct Rectangle
{
Point a, b;
public Rectangle(Point a, Point b) {
this.a = a;
this.b = b;
}
public Point A {
get { return a; }
set { a = value; }
}
public Point B {
get { return b; }
set { b = value; }
240
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
このとき,次の例を考える。
Point p = new Point();
p.X = 100;
p.Y = 100;
Rectangle r = new Rectangle();
r.A = new Point(10, 10);
r.B = p;
p.X,p.Y,r.A及びr.Bへの代入は,p及びrが変数なので,許される。しかし,次の例を
考える。
Rectangle r = new Rectangle();
r.A.X = 10;
r.A.Y = 10;
r.B.X = 100;
r.B.Y = 100;
この例では,r.A及びr.Bは変数ではないので,代入はすべて有効ではない。
14.14.2
複合代入
x op= yの形式の演算は,その演算がx op yと書かれているものとして,2項演算子多重定義解決(14.2.4
参照)を適用することによって処理される。そのとき,次のとおりとする。
− 選択された演算子の返却型がxの型に暗黙に変換される場合,演算は,x = x op yとして評価され
る。ただし,xは一度だけ評価される。
− そうでない場合であって,選択された演算子があらかじめ定義された演算子であり,選択された演算
子の返却型がxの型に明示的に変換され,yがxの型に暗黙に変換される場合,又は演算子がシフト
演算子の場合,その演算はx = (T)(x op y)として評価される。ここで,Tはxの型とし,xは一度
だけ評価される。
− そうでなければ,その複合代入は無効であって,コンパイル時エラーになる。
“一度だけ評価される”という言葉は,x op yの評価において,xのあらゆる構成要素の式の結果が一
時的に保存され,xへの代入を実行するときに再使用されることを意味する。
例 例えば,Aをint[]を返すメソッドとし,B及びCをintを返すメソッドとする場合,代入
A()[B()] += C()において,メソッドは,A,B及びCの順番で一度だけ呼び出される。
複合代入の左演算対象が特性アクセス又は添字子アクセスの場合,その特性又は添字子は,getアクセ
ス子及びsetアクセス子の両方をもたなければならない。そうでない場合には,コンパイル時エラーにな
る。
上記の2番目の規則は,ある文脈ではx op= yをx = (T)(x op y)として評価することを許可する。
左演算対象の型がsbyte,byte,short,ushort又はcharの場合,あらかじめ定義された演算子を複
合演算子として用いることができるといった規則が存在する。実引数の両方がそれらの型の一つの場合で
あっても,14.2.6.2で示されるとおり,あらかじめ定義された演算子が型intの結果を生成する。したが
って,キャストを行わないと,結果を左演算対象に代入できないことになる。
あらかじめ定義された演算子についてのこの規則の直感的な効果は,単に,x op y及びx = yの両方
241
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
が許可される場合にはx op= yが許可されるということになる。
例 次に例を示す。
byte b = 0;
char ch = '¥0';
int i = 0;
b += 1;
// Ok
b += 1000;
// Error, b = 1000 not permitted
b += i;
// Error, b = i not permitted
b += (byte)i;
// Ok
ch += 1;
// Error, ch = 1 not permitted
ch += (char)1;
// Ok
各エラーの直感的な理由は,対応する単純代入もエラーになることによる。
注記 複合代入演算子はもち上げ演算子にも対応する。複合代入x op= yがx = x op y又はx =
(T)(x op y)のいずれかとして評価されるので,評価規則には暗黙にもち上げ演算子を網羅
している。
14.14.3
イベント代入
+=演算子又は-=演算子の左演算対象がイベントであれば,その式はイベントアクセスとして分類され,
次のとおりに評価される。
− イベントアクセスのインスタンス式が存在する場合は,それを評価する。
− +=演算子又は-=演算子の右演算対象が評価され,必要な場合には,暗黙変換(13.1参照)によって左
演算対象の型に変換される。
− 前項の段階で計算された値で構成される実引数並びを用いて,イベントのイベントアクセス子を呼び
出す。演算子が+=の場合,addアクセス子が呼び出され,演算子が-=の場合,removeアクセス子が
呼び出される。
イベント代入式は,値を生成しない。したがって,イベント代入式は,≪式文≫(15.6参照)の文脈で
だけ有効になる。
14.15
式
≪式≫は,≪二択条件式≫又は≪代入≫のいずれかとする。
≪式≫:
≪二択条件式≫
≪代入≫
14.16
定数式
≪定数式≫は,コンパイル時に完全に評価しなければならない式とする。式が定数であることが要求さ
れるところは,この文法では≪定数式≫を使って示す。
≪定数式≫:
≪式≫
定数式の型は,sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,
decimal,bool,string,任意の列挙型,又はnull型(11.2.7参照)の一つとする。定数式では,次
の構築要素が許される。
− リテラル(nullリテラルを含む。)。
242
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− クラス型及び構造型のconstメンバへの参照。
− 列挙型のメンバへの参照。
− それ自体が定数式になる括弧付き部分式。
− キャスト式。ただし,変換先の型は上記の型の一つとする。
− あらかじめ定義されたchecked,unchecked,+,‒,!及び~の単項演算子。
− あらかじめ定義された+,‒,*,/,%,<<,>>,&,|,^,&&,||,==,!=,<,>,<=,及び>=
の2項演算子。ただし,各演算対象の型は,上記の型とする。
− ?:二択条件演算子。
− sizeof式。ただし,非管理型は14.5.12で規定された型の一つとする。
− 省略時の値式。ただし,型は,上記の型の一つである型,参照型である型,又は参照型(25.7参照)
であることが知られている型仮引数とする。
定数式では,次の変換が許される。
− 恒等変換。
− 数値変換。
− 列挙変換。
− 定数式変換。
− 変換元がnull値に評価される定数式である暗黙の変換又は明示的な変換。
注記 定数式では,nullでない値のボックス化変換,ボックス化解除変換,及び暗黙の参照変換は
許されない。
例 次にコードを示す。
class C {
const object ival = 5; // error: boxing conversion not permitted
const object str = "hello"; // error: implicit reference conversion
}
ival の初期化は,ボックス化変換が要求されるのでエラーになる。strの初期化は,null
でない値からの暗黙の参照変換が必要なのでエラーになる。
≪式≫の型が上記の型の一つであって,上記の構築要素だけを含んでいる場合は常に,それは定数式で
あり,コンパイル時に式が評価される。式が定数ではない構築要素を含む,より大きな式の部分式になる
場合であっても,このことは成り立つ。
定数式のコンパイル時評価は,非定数式の実行時評価と同じ規則を用いる。ただし,実行時評価が例外
を送出する場合には,コンパイル時評価はコンパイル時エラーを引き起こす。
定数式が明示的にunchecked文脈の中に置かれていない場合,整数型算術演算及び式のコンパイル時
評価の最中の変換で発生するけた(桁)あふれは,常に,コンパイル時エラーになる(14.5.13参照)。
定数式は,次に示す文脈内で要求され,この文法では≪定数式≫を用いて示される。これらの文脈では,
コンパイル時に式を完全に評価できない場合には,コンパイル時エラーになる。
− 定数宣言(17.3参照)。
− 列挙型メンバ宣言(21.3参照)。
− switch文のcaseラベル(15.7.2参照)。
− goto case文(15.9.3参照)。
− 初期化子を含む配列生成式における次元の長さ(14.5.10.2参照)。
243
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 属性(箇条24参照)。
暗黙の定数式変換(13.1.7参照)は,型intの定数式をsbyte,byte,short,ushort,uint又は
ulongに変換することを許す。ただし,定数式の値が,目的の型の値域内にある場合に限る。
14.17
真理式
≪真理式≫は,型boolの結果を生成する式とする。
≪真理式≫:
≪式≫
≪if文≫(15.7.1参照),≪while文≫(15.8.1参照),≪do文≫(15.8.2参照)又は≪for文≫(15.8.3
参照)の制御条件式は,≪真理式≫とする。?:演算子(14.13参照)における制御条件式は,≪真理式≫と
同じ規則に従うが,演算子優先順位によって,≪null判定選択式≫として分類される。
≪真理式≫は,暗黙にboolに変換可能な型,又はoperator trueを実装する型になることが要求さ
れる。
注記 17.9.1が要求するとおり,operator trueを実装するあらゆる型は,operator falseも実
装しなければならない。
要件のすべてが満たされない場合,コンパイル時エラーになる。
≪真理式≫が,暗黙にboolに変換できないがoperator trueを実装する型の場合,式の評価に続い
て,その型が提供するoperator trueの実装が,bool値を生成するために呼び出される。
15 文
C#は多様な文を規定している。
注記 これらの文の多くはC言語及びC++言語でプログラムを作成している開発者にはよく知られて
いるものである。
≪文≫:
≪ラベル付き文≫
≪宣言文≫
≪埋込み文≫
≪埋込み文≫:
≪ブロック≫
≪空文≫
≪式文≫
≪選択文≫
≪繰返し文≫
≪飛越し文≫
≪try文≫
≪checked文≫
≪unchecked文≫
≪lock文≫
≪using文≫
≪yield文≫
非終端記号≪埋込み文≫は,文を他の文内に記述する場合に使用する。≪文≫ではなく≪埋込み文≫が
244
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
使われている文脈では宣言文及びラベル付き文は使用できない。
例 次のコードはコンパイル時エラーになる。
void F(bool b) {
if (b)
int i = 44;
}
これはif文が,if分岐用に≪文≫ではなく≪埋込み文≫を要求するからである。このコード
を許可すると,変数iは宣言されるが,使用できないことになる(ただし,iの宣言をブロック
内で行えば,上記の例は有効となることに注意する。)。
15.1 終了点及び到達可能性
すべての文には終了点がある。直感的に表現すれば,文の終了点とは,その文の直後の位置である。合
成文(埋込み文を含む文)の実行規則は,制御が埋込み文の終了点に達したときに実行される動作を指定
する。
例 制御がブロック内のある文の終了点に達したとき,制御はそのブロック内の次の文に移る。
実行によってある文に到達できる場合,その文は到達可能という。逆に,ある文が実行される可能性が
ない場合,その文は到達不能という。
例 次にコード例を示す。
void F() {
Console.WriteLine("reachable");
goto Label;
Console.WriteLine("unreachable");
Label:
Console.WriteLine("reachable");
}
Console.WriteLineの2回目の呼出しは,この文が実行される可能性が全くないので到達不
能とする。
コンパイラは,ある文が到達不能であると判定すると警告を発する。文が到達不能でもエラーになるわ
けではない。
注記 ある特定の文又は終了点が到達可能かどうかを判定するために,コンパイラは各々の文に対し
て定義された到達可能性規則に従ってフロー解析を行う。フロー解析は文の振る舞いを制御す
る定数式(14.16参照)の値は考慮するが,非定数式がとりうる値については考慮しない。すな
わち,制御フロー解析においては,指定された型の非定数式は,その型がとることのできるあ
らゆる値をもつものとみなされる。
次に例を示す。
void F() {
const int i = 1;
if (i == 2) Console.WriteLine("unreachable");
}
このif文の真理式は,==演算子の両方の演算対象が定数であるので定数式とする。コンパ
イル時にこの定数式が評価された時点で,falseとなり,Console.WriteLineの呼出しは
245
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
到達不能とみなされる。しかし,次のようにiを局所変数に変えた場合,実際には決して実行
されないとしてもConsole.WriteLineは到達可能とみなされる。
void F() {
int i = 1;
if (i == 2) Console.WriteLine("reachable");
}
関数メンバの(メソッド本体の)ブロックは常に到達可能とみなされる。≪無名メソッド式≫のブロッ
クは常に到達可能とみなされる。ブロック内の各文の到達可能性規則を連続して評価することで,任意の
文の到達可能性を決定できる。
例 次にコード例を示す。
void F(int x) {
Console.WriteLine("start");
if (x < 0) Console.WriteLine("negative");
}
2番目のConsole.WriteLineの到達可能性は,次のように判定する。
− 1番目のConsole.WriteLine式文は,メソッドFのブロックが到達可能(15.2参照)である
ので,到達可能とする。
− 1番目のConsole.WriteLine式文の終了点は,その文が到達可能(15.6及び15.2参照)であ
るので,到達可能とする。
− if文は,1番目のConsole.WriteLine式文の終了点が到達可能(15.6及び15.2参照)である
ので,到達可能とする。
− 2番目のConsole.WriteLine式文は,if文の真理式が定数値falseをもたないので,到達可
能とする。
次の二つの場合には,文の終了点が到達可能になるとコンパイル時エラーになる。
− switch文はあるswitch節から次のswitch節に処理を継続させないので,switch節の文並びの
終了点が到達可能になるとコンパイル時エラーになる。このエラーが起こる場合,典型的にはbreak
文がないことを示している。
− 値を計算する関数メンバのブロックの終了点が到達可能になると,コンパイル時エラーになる。この
エラーが起こる場合,典型的にはreturn文がないことを示している。
15.2 ブロック
≪ブロック≫を使うと,一つの文が許容される文脈に複数の文を記述できるようになる。
≪ブロック≫:
{ ≪文並び≫opt }
≪ブロック≫は波括弧で囲まれた省略可能な≪文並び≫(15.2.1参照)で構成される。文並びを省略す
ると,ブロックは空になる。
ブロックは宣言文(15.5参照)を含むことができる。ブロック内で宣言された局所変数又は局所定数の
有効範囲は,そのブロックとする。
ブロック内では,式文脈又は宣言子文脈の中で使われた名前の意味は常に一定でなければならない
(14.5.2.1参照)。
ブロックは次のように実行される。
246
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− ブロックが空の場合,制御はブロックの終了点に移る。
− ブロックが空でない場合,制御は文並びに移る。制御が文並びの終了点に達すると,そのブロックの
終了点に制御が移る。
ブロック自体が到達可能な場合,ブロックの文並びは到達可能とする。
ブロックが空か又は文並びの終了点が到達可能な場合,ブロックの終了点は到達可能とする。
15.2.1 文並び
文並びは連続して書かれた一つ以上の文で構成される。≪ブロック≫(15.2参照)及び≪switchブロ
ック≫(15.7.2参照)内に記述できる。
≪文並び≫:
≪文≫
≪文並び≫ ≪文≫
文並びは,その最初の文に制御を移すことで実行される。制御が文の終了点に達すると,次の文に制御
が移る。制御が最後の文の終了点に達すると,その文並びの終了点に制御が移る。
文並び内の各文は,次のいずれか一つでも当てはまれば到達可能とする。
− 文が最初の文であり,文並び自体が到達可能な場合。
− 前の文の終了点が到達可能な場合。
− 文がラベル付き文であり,到達可能なgoto文によってそのラベルが参照される場合。
文並びの最後の文の終了点が到達可能な場合は,文並びの終了点は到達可能とする。
15.3 空文
≪空文≫は何もしない。
≪空文≫:
;
空文は,文が必要となる文脈で,実行する操作がない場合に使用される。
空文の実行では,文の終了点に制御が移るだけとする。このため,空文が到達可能な場合,空文の終了
点は到達可能とする。
例 空文は本体をもたないwhile文を記述するときに使用できる。
bool ProcessMessage() {…}
void ProcessMessages() {
while (ProcessMessage())
;
}
空文はブロックの閉じ“}”の直前にラベルを宣言する場合にも使用できる。
void F() {
…
if (done) goto exit;
…
exit: ;
}
15.4 ラベル付き文
≪ラベル付き文≫を使うと,文の前にラベルを付けることができる。ラベル付き文はブロック内で使用
247
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
できるが,埋込み文としては使用できない。
≪ラベル付き文≫:
≪識別子≫ : ≪文≫
ラベル付き文では,≪識別子≫によって指定された名前のラベルを宣言する。ラベルの有効範囲は,入
れ子になったブロックを含め,そのラベルが宣言されたブロック全体とする。二つの同じ名前のラベルが
重複した有効範囲をもつと,コンパイル時エラーになる。
ラベルはそのラベルの有効範囲内のgoto文(15.9.3参照)から参照できる。
注記 これはgoto文がブロック内及びブロックの外に制御を移すことはできるが,ブロックの外か
らブロックの内部に制御を移すことはできないことを意味する。
ラベルには固有の宣言空間があり,それぞれの識別子で干渉することはない。
例 次に例を示す。
int F(int x) {
if (x >= 0) goto x;
x = -x;
x: return x;
}
この例では名前xを仮引数及びラベルの両方に用いているが,有効とする。
ラベル付き文の実行は,ラベルの次の文の実行と正確に一致する。
通常の制御のフローによって提供される到達可能性に加えて,到達可能なgoto文によってラベルが参
照される場合,ラベル付き文は到達可能とする。ただし,goto文が,終了点が到達不能なfinallyブロ
ックを含むtryの内側にあって,かつ,そのラベル付き文がtryの外側にある場合を除く。
15.5
宣言文
≪宣言文≫は局所変数又は局所定数を宣言する。宣言文はブロック内で使用できるが,埋込み文として
は使用できない。
≪宣言文≫:
≪局所変数宣言≫ ;
≪局所定数宣言≫ ;
15.5.1 局所変数宣言
≪局所変数宣言≫は一つ以上の局所変数を宣言する。
≪局所変数宣言≫:
≪型≫ ≪局所変数宣言子群≫
≪局所変数宣言子群≫:
≪局所変数宣言子≫
≪局所変数宣言子群≫ , ≪局所変数宣言子≫
≪局所変数宣言子≫:
≪識別子≫
≪識別子≫ = ≪局所変数初期化子≫
≪局所変数初期化子≫:
≪式≫
≪配列初期化子≫
248
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪局所変数宣言≫の≪型≫は,この宣言で導入される変数の型を指定する。型の後には,≪局所変数宣
言子≫の並びが続き,各局所変数宣言子によって新しい変数が導入される。≪局所変数宣言子≫は,変数
を名前付ける≪識別子≫で構成される。識別子の後に字句“=”と,変数の初期値を指定する≪局所変数
初期化子≫を続けることもできる。
局所変数の値は≪単純名≫(14.5.2参照)を使った式の中で取得され,局所変数の値は≪代入≫(14.13
参照)を使って変更される。局所変数は,その値が取得される時点で明示的に代入されていなければなら
ない(12.3参照)。
≪局所変数宣言≫で宣言される局所変数の有効範囲は,宣言が行われているブロックとする。局所変数
の≪局所変数宣言子≫より前の記述位置でその局所変数を参照するとエラーになる。ある局所変数の有効
範囲内で,同じ名前をもつ別の局所変数や局所定数を宣言するとコンパイル時エラーになる。
複数の変数を宣言する局所変数宣言は,同一型の単一の変数を複数回宣言するのと同等とする。さらに,
局所変数宣言の変数初期化子は,宣言直後に挿入された代入文と完全に一致する。
例 次に例を示す。
void F() {
int x = 1, y, z = x * 2;
}
この例は次の式と完全に一致する。
void F() {
int x; x = 1;
int y;
int z; z = x * 2;
}
15.5.2 局所定数宣言
≪局所定数宣言≫は一つ以上の局所定数を宣言する。
≪局所定数宣言≫:
const ≪型≫ ≪定数宣言子群≫
≪定数宣言子群≫:
≪定数宣言子≫
≪定数宣言子群≫ , ≪定数宣言子≫
≪定数宣言子≫:
≪識別子≫ = ≪定数式≫
≪局所定数宣言≫の≪型≫は,この宣言で導入される定数の型を指定する。この型の後には,≪定数宣
言子≫の並びが続き,各定数宣言子によって新しい定数が導入される。≪定数宣言子≫は定数に名前を与
える≪識別子≫,それに続く字句“=”,それに続く定数の値を与える≪定数式≫(14.15参照)で構成さ
れる。
局所定数宣言の≪型≫及び≪定数式≫は,定数メンバ宣言(17.3参照)の規則と同じ規則に従わなけれ
ばならない。
局所定数の値は≪単純名≫(14.5.2参照)を使った式の中で取得される。
局所定数の有効範囲は,宣言が行われているブロックとする。≪定数宣言子≫より前の記述位置でその
局所定数を参照するとエラーになる。ある局所定数の有効範囲内で,同じ名前の別の局所変数や局所定数
249
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
を宣言するとコンパイル時エラーになる。
複数の定数を宣言する局所定数宣言は,同一型の単一の定数を複数回宣言するのと同等とする。
15.6 式文
≪式文≫は指定された式を評価する。式で計算される値があっても,値は破棄される。
≪式文≫:
≪文式≫ ;
≪文式≫:
≪呼出し式≫
≪オブジェクト生成式≫
≪代入≫
≪後置増加式≫
≪後置減少式≫
≪前置増加式≫
≪前置減少式≫
文として使用できない式もある。
注記 特に,x + y及びx == 1のような,破棄される値を計算するだけの式は文としては認められ
ない。
式文の実行によって,含まれる式が評価され,式文の終了点に制御が移る。≪式文≫が到達可能な場合,
≪式文≫の終了点は到達可能とする。
15.7 選択文
選択文は,式の値に基づいて,複数の実行可能な文から,一つの文を選択する。
≪選択文≫:
≪if文≫
≪switch文≫
15.7.1 if文
if文は真理式の値に基づいて,実行する文を選択する。
≪if文≫:
if ( ≪真理式≫ ) ≪埋込み文≫
if ( ≪真理式≫ ) ≪埋込み文≫ else ≪埋込み文≫
else部分は字句解析的に最も近い位置にある文法的に許容されるif文と関連付けられる。
例 したがって,次の二つのif文は,同じ意味になる。
if (x) if (y) F(); else G();
if (x) {
if (y) {
F();
}
else {
G();
}
250
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
if文は,次のとおり実行される。
− ≪真理式≫(14.17参照)が評価される。
− 真理式がtrueになった場合,最初の埋込み文に制御が移る。制御がその文の終了点に達すると,if
文の終了点に制御が移る。
− 真理式がfalseになりelse部分が存在する場合,制御は2番目の埋込み文に制御が移る。制御がそ
の文の終了点に達すると,if文の終了点に制御が移る。
− 真理式がfalseになりelse部分が存在しない場合,if文の終了点に制御が移る。
if文の最初の埋込み文はそのif文が到達可能,かつ,真理式が定数値のfalseでない場合,到達可
能とする。
if文の2番目の埋込み文が存在する場合,その埋込み文はif文が到達可能,かつ,真理式が定数値の
trueをもたない場合,到達可能とする。
if文の終了点は,少なくとも一つの埋込み文の終了点が到達可能であるとき,到達可能とする。さらに,
else部分をもたないif文の終了点は,if文が到達可能,かつ,真理式が定数値のtrueをもたない場
合,到達可能とする。
15.7.2 switch文
switch文は,switch式の値に一致するswitchラベルをもつ文並びを選択して実行する。
≪switch文≫:
switch ( ≪式≫ ) ≪switchブロック≫
≪switchブロック≫:
{ ≪switch節群≫opt }
≪switch節群≫:
≪switch節≫
≪switch節群≫ ≪switch節≫
≪switch節≫:
≪switchラベル群≫ ≪文並び≫
≪switchラベル群≫:
≪switchラベル≫
≪switchラベル群≫ ≪switchラベル≫
≪switchラベル≫:
case ≪定数式≫ :
default :
≪switch文≫は,キーワードswitch,(switch式と呼ぶ)括弧付き式,≪switchブロック≫で構
成される。≪switchブロック≫は0個以上の≪switch節群≫で構成され,波括弧で閉じられる。各≪
switch節≫は1個以上の≪switchラベル≫とそれに続く≪文並び≫(15.2.1参照)で構成される。
switch文の管理型はswitch式によって設定される。switch式の型がsbyte,byte,short,ushort,
int,uint,long,ulong,char,string又は≪列挙型≫の場合,その型がswitch文の管理型にな
る。それ以外の型の場合,switch式の型又はその基底型からsbyte,byte,short,ushort,int,
uint,long,ulong,char,stringのいずれかへの利用者定義の暗黙の変換演算子(13.4参照)が一
つだけ存在していなければならない。そういった暗黙の変換演算子が存在しない場合,又は複数存在する
251
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
場合は,コンパイル時エラーになる。
各々のラベルcaseの定数式はswitch文の管理型に暗黙に変換可能な(13.1参照)型の値になってい
なければならない。同じswitch文の二つ以上のラベルcaseに同じ定数値が指定されていた場合,コン
パイル時エラーになる。
一つのswitch文には高々1個のdefaultラベルがあってもよい。
switch文は次のとおり実行される。
− switch式が評価されて管理型に変換される。
− そのswitch文のcaseラベルに指定された定数の一つがswitch式の値と等しい場合,合致した
caseラベルに続く文並びに制御が移る。
− そのswitch文のcaseラベルに指定されたいずれの定数もswitch式の値に等しくなく,default
ラベルが存在している場合,defaultラベルに続く文並びに制御が移る。
− そのswitch文のcaseラベルに指定されたいずれの定数もswitch式の値に等しくなく,default
ラベルが存在しない場合,switch文の終了点に制御が移る。
switch節の文並びの終了点が到達可能な場合は,コンパイル時エラーになる。これは“通り抜け禁止”
規則と呼ばれる。
例 次に例を示す。
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
default:
CaseOthers();
break;
}
この例は,switch節に到達可能な終了点がないので有効とする。C及びC++とは異なり,
switch節の実行は,次のswitch節に“通り抜け”できない。次に例を示す。
switch (i) {
case 0:
CaseZero();
case 1:
CaseZeroOrOne();
default:
CaseAny();
}
この文はコンパイル時エラーになる。あるswitch節の実行に続けて別のswitch節を実行し
たい場合,明示的なgoto case又はgoto default文を使用しなくてはならない。
switch (i) {
252
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
case 0:
CaseZero();
goto case 1;
case 1:
CaseZeroOrOne();
goto default;
default:
CaseAny();
break;
}
≪switch節≫では複数のラベルを使用できる。
例 次に例を示す。
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
default:
CaseTwo();
break;
}
この例は有効となる。この例は,ラベルcase 2とdefaultとが同じ≪switch節≫の一部
であるので,“通り抜け禁止”規則に違反しない。
注記 “通り抜け禁止”規則は,誤ってbreak文を省略してしまった場合にC及びC++で起こる,
よくあるバグを予防する。また,この規則によって,switch文のswitch節は,文の振る舞
いに影響を与えることなく,自由に再配置できる。例えば,二つ前のswitch文の各節を逆順
に配置しても,文の振る舞いに影響を与えない。
switch (i) {
default:
CaseAny();
break;
case 1:
CaseZeroOrOne();
goto default;
case 0:
CaseZero();
goto case 1;
253
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
注記 switch節の文並びは典型的にはbreak,goto case又はgoto default文で終わる。ただ
し,文並びの終了点を到達不能にする構築要素は許可される。例えば真理式がtrueのwhile
文はその終了点に到達できないことが知られている。同様に,throw文及びreturn文は常に
他の箇所に制御を移し,その終了点に到達することはない。したがって,次の例は有効となる。
switch (i) {
case 0:
while (true) F();
case 1:
throw new ArgumentException();
case 2:
return;
}
例 switch文の管理型は型stringであってもよい。次に例を示す。
void DoCommand(string command) {
switch (command.ToLower()) {
case "run":
DoRun();
break;
case "save":
DoSave();
break;
case "quit":
DoQuit();
break;
default:
InvalidCommand(command);
break;
}
}
注記 文字列等価演算子(14.9.7参照)と同様に,switch文は大文字と小文字を区別し,switch式
の文字列が正確にcaseラベル定数と一致する場合にだけ,指定されたswitch節を実行する。
switch文の管理型がstringの場合,値nullはcaseラベル定数として許される。
≪switchブロック≫の≪文並び≫は宣言文(15.5参照)を含んでもよい。switchブロック内で宣言
された局所変数又は局所定数の有効範囲はそのswitchブロックとする。
switchブロックの範囲内で,式の文脈内で使用された名前の意味は一定でなければならない(14.5.2.1
参照)。
switch節の文並びが到達可能になるのは,switch文が到達可能であり,次のいずれか一つでも当て
はまる場合とする。
− switch式が非定数値である場合。
254
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− switch式が,switch節のcaseラベルに一致する定数値である場合。
− switch式がどのcaseラベルにも一致しない定数値であり,かつ,そのswitch節がdefaultラ
ベルを含む場合。
− switch節のswitchラベルが到達可能なgoto case又はgoto default文によって参照されてい
る場合。
switch文の終了点は次の条件の少なくとも一つが真の場合,到達可能とする。
− switch文がそのswitch文から抜け出す到達可能なbreak文を含む場合。
− switch文が到達可能であり,switch式が非定数値,かつ,defaultラベルが存在しない場合。
− switch文が到達可能であり,switch式がどのcaseラベルにも一致しない定数値であり,かつ,
defaultラベルが存在しない場合。
15.8 繰返し文
繰返し文は埋込み文を繰り返し実行する。
≪繰返し文≫:
≪while文≫
≪do文≫
≪for文≫
≪foreach文≫
15.8.1 while文
while文は条件付きで埋込み文を0回以上実行する。
≪while文≫:
while ( ≪真理式≫ ) ≪埋込み文≫
while文は次のとおり実行される。
− ≪真理式≫(14.17参照)を評価する。
− 真理式がtrueの場合,埋込み文に制御が移る。制御が埋込み文の終了点に達すると(continue文
の実行による場合もある),制御はwhile文の開始位置に移る。
− 真理式がfalseの場合,制御はwhile文の終了点に移る。
while文の埋込み文の範囲内で,while文の終了点に制御を移すために(すなわち埋込み文の繰返しを
終了させるために)break文(15.9.1参照)を使うことができる。また,埋込み文の終了点に制御を移す
ために(すなわちwhile文の次の繰返しを実行するために)continue文(15.9.2参照)を使うことがで
きる。
while文の埋込み文は,while文が到達可能で真理式が定数値falseをもたない場合に到達可能とす
る。
while文の終了点は次の少なくとも一つが真の場合,到達可能とする。
− while文がそのwhile文から抜け出す到達可能なbreak文を含む場合。
− while文が到達可能で真理式が定数値trueをもたない場合。
15.8.2 do文
do文は条件付きで埋込み文を1回以上実行する。
≪do文≫:
do ≪埋込み文≫ while ( ≪真理式≫ ) ;
do文は次のとおり実行される。
255
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 埋込み文に制御が移る。
− 制御が埋込み文の終了点に移ると(continue文の実行による場合もある),≪真理式≫(14.17参照)
が評価される。真理式がtrueの場合,制御はdo文の開始位置に移る。そうでなければ,制御はdo
文の終了点に移る。
do文の埋込み文の範囲内では,do文の終了点に制御を移すために(すなわち埋込み文の繰返しを終了
させるために)break文(15.9.1参照)を使うことができる。また,埋込み文の終了点に制御を移すため
に(すなわちdo文の次の繰返しを実行するために)continue文(15.9.2参照)を使うことができる。
do文の埋込み文は,do文が到達可能な場合,到達可能とする。
do文の終了点は,次の少なくとも一つが真の場合,到達可能とする。
− do文がそのdo文から抜け出す到達可能なbreak文を含む場合。
− 埋込み文の終了点が到達可能で真理式が定数値trueをもたない場合。
15.8.3 for文
for文は,初期化式の列を評価し,条件が真の間,埋込み文の実行及び反復式の列の評価を繰り返す。
≪for文≫:
for ( ≪for初期化子≫opt ; ≪for条件≫opt ; ≪for反復子≫opt ) ≪埋込み
文≫
≪for初期化子≫:
≪局所変数宣言≫
≪文式並び≫
≪for条件≫:
≪真理式≫
≪for反復子≫:
≪文式並び≫
≪文式並び≫:
≪文式≫
≪文式並び≫ , ≪文式≫
≪for初期化子≫が存在する場合,≪for初期化子≫は≪局所変数宣言≫(15.5.1参照)又はコンマで
区切られた≪式文≫(15.6参照)の並びで構成される。≪for初期化子≫によって宣言された局所変数の
有効範囲は,その変数の≪局所変数宣言子≫から埋込み文の終わりまでとする。この有効範囲には,≪for
条件≫及び≪for反復子≫が含まれる。
≪for条件≫が存在する場合,≪for条件≫は≪真理式≫(14.17参照)でなければならない。
≪for反復子≫が存在する場合,≪for反復子≫はコンマで区切られた≪式文≫(15.6参照)の並びで
構成されなければならない。
for文は次のとおり実行される。
− ≪for初期化子≫が存在する場合,変数初期化子又は文式は,記述された順序で実行される。この手
順は一度だけ実行される。
− ≪for条件≫が存在する場合は評価される。
− ≪for条件≫が存在しない場合,又はその評価がtrueの場合,制御は埋込み文に移る。制御が埋込
み文の終了点に到達すると(continue文の実行によることもある。),≪for反復子≫式がある場合
は順に評価され,上の手順の≪for条件≫の評価から次の繰返しが実行される。
256
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− ≪for条件≫が存在し,その評価がfalseの場合,制御はfor文の終了点に移る。
for文の埋込み文の範囲内で,for文の終了点に制御を移すために(すなわち埋込み文の繰返しを終了
させるために)break文(15.9.1参照)を使うことができる。また,埋込み文の終了点に制御を移すため
に(すなわちfor文の次の繰返しを実行するために)continue文(15.9.2参照)を使うことができる。
for文の埋込み文は,次のいずれか一つでも当てはまれば到達可能とする。
− for文が到達可能,かつ,≪for条件≫が存在しない。
− for文が到達可能,かつ,≪for条件≫が存在し,定数値falseをもたない。
次の少なくとも一つが真の場合,for文の終了点は到達可能とする。
− for文が,そのfor文から抜け出す到達可能なbreak文を含む。
− for文が到達可能,かつ,≪for条件≫が存在し定数値trueをもたない。
15.8.4 foreach文
foreach文は集団の要素を列挙し,集団の各々の要素に対して埋込み文を実行する。
≪foreach文≫:
foreach ( ≪型≫ ≪識別子≫ in ≪式≫ ) ≪埋込み文≫
foreach文の≪型≫及び≪識別子≫は文の繰返し変数を宣言する。繰返し変数は,読込み専用の局所変
数に対応し,有効範囲はその埋込み文全体とする。foreach文の実行中は,繰返し変数は,現在実行中の
繰返しの集団要素を表現する。埋込み文が(代入,++又は--演算子を使って)繰返し変数を変更しようと
した場合,又はref仮引数若しくはout仮引数として渡そうとした場合,コンパイル時エラーになる。
foreach文のコンパイル時の処理は,まず,集団型,列挙型及び式の要素型を決定することから始まる。
この決定は次のように行われる。
− ≪式≫の型Xが配列型の場合,System.ArrayがSystem.Collections.IEnumerableインタフ
ェースを実装しているため,XからSystem.Collections.IEnumerableインタフェースへの暗黙
の参照変換が存在する。集団型はSystem.Collections.IEnumerableインタフェースとし,列挙
型はSystem.Collections.IEnumeratorインタフェースとし,要素型は配列型Xの要素の型と
する。
− そうでなければ,次のようにして,型Xが適切なGetEnumeratorメソッドをもっているかどうかを
調べる。
・ 型Xのメンバの中から,型実引数を指定せずに識別子GetEnumeratorを検索する。メンバ検索で
一致するものが見つからない場合,メンバ検索の結果があいまいな場合,又は,メソッドグループ
ではないメンバが見つかった場合,後ろの細別(次の−で始まる細別)で説明する方法で列挙可能
なインタフェースを検査する。メンバ検索の結果がメソッドグループ以外のものになった場合,及
びメソッド検索で何も見つからなかった場合には,警告を出すことを推奨する。
・ メンバ検索で得られたメソッドグループ及び空の実引数並びを使って多重定義解決を実行する。多
重定義解決の結果,適用可能なメソッドがなかった場合,多重定義解決の結果があいまいな場合,
又は,多重定義解決によって最善のメソッドが一つに決まったが,そのメソッドが静的か,若しく
は,公開ではない場合,別の箇条で説明する方法で列挙可能なインタフェースを検査する。多重定
義解決の結果があいまいさのない公開のインスタンスメソッド以外のものになった場合,又は,多
重定義解決によって適用可能なメソッドが見つからなかった場合には,警告を出すことを推奨する。
・ GetEnumeratorメソッドの返却値の型Eがクラス型,構造体型又はインタフェース型ではなかっ
た場合,エラーとし,以降の処理段階は実行しない。
257
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
・ Eのメンバの中から,型実引数を指定せずに識別子Currentを検索する。メンバ検索の結果,一
致するものが見つからない場合,検索結果がエラーになった場合,又は,検索結果が読込み可能な
公開インスタンス特性以外のものになった場合,エラーとし,以降の処理段階は実行しない。
・ Eのメンバの中から,型実引数を指定せずに識別子MoveNextを検索する。メンバ検索の結果,一
致するものが見つからなかった場合,検索結果がエラーになった場合,又は,検索結果がメソッド
グループ以外のものになった場合,エラーとし,以降の処理段階は実行しない。
・ メンバ検索の結果得られたメソッドグループに空の実引数リストを指定して,多重定義解決を実行
する。多重定義解決の結果,適用可能なメソッドが見つからなかった場合,多重定義解決の結果が
あいまいになった場合,又は,多重定義解決の結果,最善のメソッドが一つに決まったが,そのメ
ソッドが静的であるか,公開でないか,返却値の型がboolでない場合,エラーとし,以降の処理
段階は実行しない。
・ 集団型をX,列挙型をE,要素型をCurrent特性の型とする。
− そうでなければ,列挙可能なインタフェースをもっているかどうか調べる。
・ XからインタフェースSystem.Collections.Generic.IEnumerable<T>への暗黙の変換があ
るような型Tが一つだけ存在する場合,集団型はそのインタフェース,列挙型はインタフェース
System.Collections.Generic.IEnumerator<T>,要素型はTとする。
・ そうでない場合であって,上記のような型Tが複数存在する場合,エラーとし,以降の処理段階は
実行しない。
・ そうでない場合であって,XからインタフェースSystem.Collections.IEnumerableへの暗黙
の変換が存在する場合,集団型はそのインタフェース,列挙型はインタフェース
System.Collections.IEnumerator,要素型はobjectとする。
・ そうでなければ,エラーとし,以降の処理段階は実行しない。
上記の処理が成功した場合,あいまいさなく,集団型C,列挙型E及び要素型Tが得られる。
foreach (V v in x) ≪埋込み文≫
このとき,上記の形式のforeach文は次のように展開される。
{
E e = ((C)(x)).GetEnumerator();
try {
V v;
while (e.MoveNext()) {
v = (V)(T)e.Current;
≪埋込み文≫
}
}
finally {
… // Dispose e
}
}
変数eは,式x,≪埋込み文≫及びプログラムの他のいかなるソースコードからも可視ではなく,かつ,
アクセス可能でもない。変数vは≪埋込み文≫内で読取り専用とする。要素型Tからforeach文の型V
258
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
への明示的な変換(13.2参照)が存在しない場合,エラーとし,以降の処理段階は実行しない。
注記 xの値がnullの場合,実行時にSystem.NullReferenceExceptionが送出される。
finallyブロックの本体は次の処理手順に沿って構築される。
− EからSystem.IDisposableインタフェースへの暗黙の変換が存在する場合,finally節は次の
コードと意味論的に同等なものに展開される。
finally {
((System.IDisposable)e).Dispose();
}
ただし,eが値型又は値型で具現化される型仮引数の場合に,eからSystem.IDisposableへの
キャストが,本来起こるはずのボックス化を生じない点を除く。
− そうでない場合であって,Eが封印型の場合,finally節は次のように空のブロックに展開される。
finally {
}
− そうでないならば,finally節は次のように展開される。
finally {
System.IDisposable d = e as System.IDisposable;
if (d != null) d.Dispose();
}
局所変数dは利用者が記述したいかなるコードからも可視ではなく,かつ,アクセス可能でもない。
また,有効範囲がfinallyブロックを含む他の任意の変数と衝突を起こさない。
foreachが配列の要素をたどる順序は次のとおりとする。1次元配列の要素については,添字0から始
めて添字Length ‒ 1で終わるまで,添字を増加させる順序でたどる。多次元配列については,まず最も
右端の次元の添字を増加させ,次にその左隣の次元の添字を増加させ,更にその左というような順序で要
素をたどる。
例 2次元配列のそれぞれの値を要素順に印刷出力するコード例を次に示す。
using System;
class Test
{
static void Main() {
double[,] values = {
{1.2, 2.3, 3.4, 4.5},
{5.6, 6.7, 7.8, 8.9}
};
foreach (int elementValue in values)
Console.Write("{0} ", elementValue);
Console.WriteLine();
}
}
生成される出力は次のとおりになる。
259
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
1 2 3 4 5 6 7 8
15.9 飛越し文
飛越し文は無条件に制御を移す。
≪飛越し文≫:
≪break文≫
≪continue文≫
≪goto文≫
≪return文≫
≪throw文≫
飛越し文が制御を移す先の場所を,飛越し文の飛び先と呼ぶ。
ブロック範囲内で飛越し文が実行され,その飛越し文の飛び先がそのブロックの外側にある場合,その
飛越し文はそのブロックを抜け出すという。飛越し文はブロックの外に制御を移すことができるが,ブロ
ックの内部に制御を移すことはできない。
飛越し文の実行は間に挟まったtry文の存在によって,複雑になる。飛越し文の間にtry文がない場
合は,無条件に制御を飛越し文から飛び先に移す。飛越し文の間にtry文がある場合は,実行はより複雑
になる。飛越し文がfinallyブロックと関連付けられた1個以上のtryブロックを抜け出す場合,最初
に最内側のtry文のfinallyブロックに制御が移る。制御がfinallyブロックの終了点に達すると,
制御は取り囲んでいる次のtry文のfinallyブロックに移る。この処理は間に挟まったすべてのtry文
のfinallyブロックを実行するまで繰り返される。
例 次にコード例を示す。
using System;
class Test
{
static void Main() {
while (true) {
try {
try {
Console.WriteLine("Before break");
break;
}
finally {
Console.WriteLine("Innermost finally
block");
}
}
finally {
Console.WriteLine("Outermost finally block");
}
}
Console.WriteLine("After break");
260
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
制御が飛越し文の飛び先に移る前に,二つのtry文に関連付けられたfinallyブロックが実
行される。
生成される出力は次のとおりとなる。
Before break
Innermost finally block
Outermost finally block
After break
15.9.1 break文
break文は,そのbreak文を取り囲んでいる最も近くのswitch文,while文,do文,for文又は
foreach文を抜け出す。
≪break文≫:
break ;
break文の飛び先は,そのbreak文を取り囲んでいる最も近くのswitch文,while文,do文,for
文又はforeach文の終了点とする。break文がswitch文,while文,do文,for文又はforeach
文によって取り囲まれていない場合,コンパイル時エラーになる。
複数のswitch文,while文,do文,for文又はforeach文が互いに入れ子になっている場合,break
文は最内側の文にだけ適用される。複数の入れ子水準を越えて制御を移すには,goto文(15.9.3参照)を
使わなければならない。
break文はfinallyブロック(15.10参照)を抜け出すことはできない。break文がfinallyブロッ
クで実行される場合,break文の飛び先は同じfinallyブロック内に存在しなければならない。そうで
なければコンパイル時エラーになる。
break文は次のとおりに実行される。
− break文が関連するfinallyブロックをもつ1個以上のtryブロックを抜け出す場合,制御は最初
に最内側のtry文のfinallyブロックに移される。制御がfinallyブロックの終了点に達すると,
制御は取り囲んでいるtry文の次のfinallyブロックに制御が移る。この処理は間に挟まったすべ
てのtry文のfinallyを実行するまで繰り返される。
− 制御がbreak文の飛び先に移る。
break文は無条件に別の場所に制御を移すので,break文の終了点は決して到達可能にならない。
15.9.2 continue文
continue文はそのcontinue文を取り囲んでいる最も近いwhile文,do文,for文又はforeach
文の新しい繰返しを開始する。
≪continue文≫:
continue ;
continue文の飛び先はそのcontinue文を取り囲んでいる最も近いwhile文,do文,for文又は
foreach文の埋込み文の終了点とする。continue文がwhile文,do文,for文又はforeach文で取
り囲まれていない場合,コンパイル時エラーになる。
複数のwhile文,do文,for文又はforeach文が互いに入れ子になっている場合,continue文は
最内側の文だけに適用される。複数の入れ子水準を越えて制御を移すには,goto文(15.9.3参照)を使わ
261
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
なければならない。
continue文はfinallyブロック(15.10参照)から抜け出すことはできない。continue文がfinally
ブロック内で実行される場合,continue文の飛び先は同じfinallyブロック内に存在しなければなら
ない。そうでなければコンパイル時エラーになる。
continue文は次のとおりに実行される。
− continue文が関連するfinallyブロックをもつ1個以上のtryブロックを抜け出す場合,制御は
最初に最内側のtry文のfinallyブロックに移される。制御がfinallyブロックの終了点に達す
ると,制御は取り囲んでいる次のtry文のfinallyブロックに移る。この処理は間に挟まったすべ
てのtry文のfinallyブロックを実行するまで繰り返される。
− 制御がcontinue文の飛び先に移る。
continue文は無条件に制御を別の場所に移すので,continue文の終了点は決して到達可能にならな
い。
15.9.3 goto文
goto文はラベルによって印付けられた文に制御を移す。
≪goto文≫:
goto ≪識別子≫ ;
goto case ≪定数式≫ ;
goto default ;
goto ≪識別子≫文の飛び先は指定されたラベルをもつラベル付き文とする。指定された名前のラベル
が現在の関数メンバ内に存在しない場合,又はgoto文がラベルの有効範囲内に存在しない場合は,コン
パイル時エラーになる。
注記 この規則は入れ子になった有効範囲の外側に制御を移すためにgoto文を使用することは許可
するが,入れ子になった有効範囲の内側に制御を移すことは許可しない。次に例を示す。
using System;
class Test
{
static void Main(string[] args)
string[,] table = {
{"red", "blue", "green"},
{"Monday", "Wednesday", "Friday"}
};
foreach (string str in args) {
int row, colm;
for (row = 0; row <= 1; ++row)
for (colm = 0; colm <= 2; ++colm)
if (str == table[row,colm])
goto done;
Console.WriteLine("{0} not found", str);
continue;
done:
262
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine("Found {0} at [{1}][{2}]", str, row,
colm);
}
}
}
goto文は入れ子になった有効範囲の外側に制御を移すために使われる。
このgoto case文の飛び先は,goto case文を直接取り囲んでいるswitch文(15.7.2参照)中の指
定された定数値をもつcaseラベルを含む,文並びとする。goto case文がswitch文で取り囲まれて
いない場合,≪定数式≫が取り囲んでいる最も近いswitch文の管理型に暗黙に変換可能(13.1参照)で
ない場合,又は取り囲んでいる最も近いswitch文が指定された定数値をもつcaseラベルを含まない場
合,コンパイル時エラーになる。
goto default文の飛び先は,そのgoto default文を直接取り囲むswitch文(15.7.2参照)の
defaultラベルを含む文並びとする。goto default文がswitch文で取り囲まれていない場合,又は
取り囲んでいる最も近いswitch文がdefaultラベルを含まない場合には,コンパイル時エラーになる。
goto文はfinallyブロック(15.10参照)から抜け出すことはできない。goto文がfinallyブロッ
ク内に現れた場合,そのgoto文の飛び先は同じfinallyブロックの範囲内に存在しなければならない。
それ以外の場合はコンパイル時エラーになる。
goto文は次のとおりに実行される。
− goto文が関連するfinallyブロックをもつ1個以上のtryブロックを抜け出す場合,最初に最内
側のtry文のfinallyブロックに制御が移る。制御がfinallyブロックの終了点に達した場合,
次の取り囲んでいるtry文のfinallyブロックに制御が移る。この処理は間に挟まったすべてのtry
文のfinallyブロックを実行するまで繰り返される。
− goto文の飛び先に制御が移る。
goto文は無条件に制御を別の場所に移すので,goto文の終了点は決して到達可能にならない。
15.9.4 return文
return文はそのreturn文が現れた関数メンバの呼出し側に制御を戻す。
≪return文≫:
return ≪式≫opt ;
式をもたないreturn文は,値を計算しない関数メンバ,すなわち,返却値の型がvoidのメソッド,
特性及び添字子のsetアクセス子,イベントのaddアクセス子及びremoveアクセス子,インスタンス
構築子,静的構築子又は終了化子の中でだけ使用できる。
式をもつreturn文は,値を計算する関数メンバ,すなわち,返却値の型がvoidでないメソッド,特
性及び添字子のgetアクセス子,又は,利用者定義演算子の中でだけ使用できる。式の型からその式を含
んでいる関数メンバの返却値の型への暗黙の変換(13.1参照)が存在しなければならない。
return文がfinallyブロック(15.10参照)中に現れるとコンパイル時エラーになる。
return文は次のとおりに実行される。
− return文が式を指定する場合,その式は評価されて結果の値が暗黙の変換によってそのreturn文
を含む関数メンバの返却値の型に変換される。変換結果は,呼出し側への返却値となる。
− return文が関連するfinallyブロックをもつ1個以上のtryブロックによって取り囲まれている
場合,制御は最初に最内側のtry文のfinallyブロックに移される。制御がfinallyブロックの
263
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
終了点に達すると,取り囲んでいる次のtry文のfinallyブロックに制御が移る。この処理は取り
囲んでいるすべてのtry文のfinallyブロックを実行するまで繰り返される。
− 制御が,包含する関数メンバの呼出し側に戻される。
return文は無条件に制御を別の場所に移すので,return文の終了点は決して到達可能にはならない。
15.9.5 throw文
throw文は例外を送出する。
≪throw文≫:
throw ≪式≫opt ;
式をもつthrow文は,その式を評価した値を送出する。式はSystem.Exceptionクラス型又は
System.Exceptionから派生したクラス型の値を表さなければならない。式の評価がnullとなった場
合,nullの代わりにSystem.NullReferenceExceptionを送出する。
throw文に型が型仮引数(25.1.1参照)で指定される式を指定できるのは,その型仮引数が
System.Exception(又はその下位クラス)を実効基底クラス(25.7参照)としてもつ場合に限る。
式をもたないthrow文はcatchブロック内だけで使用できる。この場合,throw文はそのcatchブ
ロックによって現在処理されている例外を再送出する。
throw文は無条件に制御を他の場所に移すので,throw文の終了点は決して到達可能にならない。
例外が送出される場合,その例外を処理可能な,取り囲んでいるtry文の中の最初のcatch節に制御
が移る。例外を送出する時点から適切な例外ハンドラに制御を移す時点までに発生する処理は,例外の伝
ぱ(播)と呼ばれる。例外の伝ぱ(播)は,例外に一致するcatch節が見つかるまで次の手順を繰り返し
評価する処理で構成される。この記述で使用する送出時点とは,初期状態で例外が送出される場所とする。
− 現在の関数メンバで,送出時点の外側にある各try文を調査する。各々の文Sに対して,最内側の
try文から始めて,最外側のtry文で終わるまで,次の手順を評価する。
・ Sのtryブロックが送出時点を取り囲んでいて,Sが1個以上のcatch節をもっている場合,その
例外に対して適切なハンドラを探し出すために現れた順にcatch節を調査する。例外型又は例外型
の基底型を指定する最初のcatch節が,一致するcatch節とみなされる。全般catch節(15.10
参照)はどんな型にでも一致するcatch節とみなされる。一致するcatch節が見つかった場合,
その例外伝ぱ(播)はそのcatch節のブロックに制御を移すことで完了する。
・ そうでない場合であって,Sのtryブロック又はcatchブロックが送出時点を取り囲んでいてS
がfinallyブロックをもっている場合,制御はそのfinallyブロックに移る。finallyブロッ
クが別の例外を送出する場合,現在の例外の処理は終了する。それ以外の場合は,制御がfinally
ブロックの終了点に到達すると,現在の例外の処理を継続する。
− 例外ハンドラが現在の関数メンバ呼出し内に見つからなかった場合,関数メンバ呼出しは終了する。
関数メンバを呼び出した文に対応する,送出時点をもつ関数メンバの呼出し側について,上の手順を
繰り返す。
− 例外処理によって現在のスレッドのすべての関数メンバ呼出しが終了する場合,すなわちスレッドに
例外ハンドラがなくなると,スレッド自体が終了する。このような終了の影響は,実装定義とする。
15.10
try文
try文はブロックを実行中に起こる例外を捕そく(捉)する機構を提供する。それに加えて,try文は
制御がtry文を離れるときに常に実行されるコードのブロックを指定する機能を提供する。
≪try文≫:
264
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
try ≪ブロック≫ ≪catch節群≫
try ≪ブロック≫ ≪catch節群≫opt ≪finally節≫
≪catch節群≫:
≪特定catch節群≫
≪特定catch節群≫opt ≪全般catch節≫
≪特定catch節群≫:
≪特定catch節≫
≪特定catch節群≫ ≪特定catch節≫
≪特定catch節≫:
catch ( ≪クラス型≫ ≪識別子≫opt ) ≪ブロック≫
≪全般catch節≫:
catch ≪ブロック≫
≪finally節≫:
finally ≪ブロック≫
try文には三つの可能な形式がある。
− tryブロックとそれに続く1個以上のcatchブロック。
− tryブロックとそれに続く1個のfinallyブロック。
− tryブロックとそれに続く1個以上のcatchブロック,それに続く1個のfinallyブロック。
catch節が≪クラス型≫を指定する場合,その型はSystem.Exception又はSystem.Exception
から派生した型でなければならない。
catch節が≪クラス型≫及び≪識別子≫の両方を指定する場合,指定された名前及び型の例外変数が宣
言される。例外変数は局所変数に対応し,その有効範囲はcatchブロック全体とする。catchブロック
の実行中は,例外変数は現在処理中の例外を表現する。確実な代入の検査においては,例外変数は全体の
有効範囲で確実に代入されているとみなされる。
catch節が例外変数名を含まない場合,catchブロック内で例外オブジェクトにアクセスすることはで
きない。
catch節の中の型として型仮引数(25.1.1参照)を指定できるのは,その型仮引数がSystem.Exception
(又はその下位クラス)を実効基底クラス(25.7参照)としてもつ場合に限る。
例外型及び例外変数名のいずれも指定しないcatch節は全般catch節と呼ばれる。一つのtry文は一
つだけ全般catch節をもつことができ,全般catch節が存在する場合には,それは最後のcatch節でな
ければならない。
注記 幾つかの環境,特に複数の言語に対応する環境では,C#コードでは生成不可能な,
System.Exceptionから派生したオブジェクトとして表現できない例外をもっているかもし
れない。そのような環境においては,そのような例外を捕そく(捉)するために全般catch
節が使われることになる。したがって,全般catch節は,他の言語からの例外をも捕そく(捉)
するかもしれないという点において,System.Exception型を指定したcatch節とは意味的
に異なっている。
例外ハンドラを探すために,catch節は字句順に検査される。catch節が,同じtryに対する先行す
るcatch節で指定された型と同じ型又はその派生型を指定していると,コンパイル時エラーになる。
注記 この制限がない場合,到達不能なcatch節が書けてしまう。
265
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
catchブロック内では,そのcatchブロックで捕そくした例外を再送出するために,式をもたない
throw文(15.9.5参照)を使うことができる。例外変数に代入しても,再送出される例外は変更されない。
例 次にコード例を示す。
using System;
class Test
{
static void F() {
try {
G();
}
catch (Exception e) {
Console.WriteLine("Exception in F: " + e.Message);
e = new Exception("F");
throw;
// re-throw
}
}
static void G() {
throw new Exception("G");
}
static void Main() {
try {
F();
}
catch (Exception e) {
Console.WriteLine("Exception in Main: " + e.Message);
}
}
}
メソッドFは例外を捕そく(捉)し,診断情報を端末に書き出す。次に,例外変数を変更し,
例外を再送出する。再送出される例外が元の例外なので,出力は次のとおりになる。
Exception in F: G
Exception in Main: G
最初のcatchブロックが例外を再送出するのではなく例外eを送出した場合,出力は次のと
おりになる。
Exception in F: G
Exception in Main: F
finallyブロックの外に制御を移すbreak文,continue文及びgoto文はコンパイル時エラーにな
る。break文,continue文又はgoto文がfinallyブロック内に記述された場合,この文の飛び先は
同じfinallyブロック内に存在しなければならない。そうでない場合はコンパイル時エラーになる。
finallyブロック内にreturn文が現れるとコンパイル時エラーになる。
266
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
try文は次のとおりに実行される。
− tryブロックに制御が移る。
− 制御がtryブロックの終了点に達した場合
・ try文がfinallyブロックをもっていれば,finallyブロックを実行する。
・ 制御がtry文の終了点に移る。
− tryブロックを実行中に例外がtry文に伝ぱ(播)した場合
・ catch節がある場合は字句的出現順に検査され,適切な例外ハンドラを見つける。例外型又は例外
型の基底型を指定する最初のcatch節が,一致するcatch節とみなされる。全般catch節はどん
な例外型にも一致するものとみなされる。一致するcatch節が見つかった場合
− 一致するcatch節が例外変数を宣言する場合,例外オブジェクトをその例外変数に代入する。
− 一致するcatchブロックに制御が移る。
− 制御がcatchブロックの終了点に達した場合
・ try文がfinallyブロックをもっていれば,そのfinallyブロックを実行する。
・ try文の終了点に制御が移る。
− catchブロックを実行中に例外がtry文に伝ぱ(播)する場合
・ そのtry文がfinallyブロックをもっていれば,そのfinallyブロックを実行する。
・ 取り囲んでいる次のtry文に例外が伝ぱ(播)する。
・ try文がcatch節をもたない場合,又は例外に一致するcatch節がない場合
− try文がfinallyブロックをもっていれば,そのfinallyブロックを実行する。
− 取り囲んでいる次のtry文に例外が伝ぱ(播)する。
finallyブロックの文並びはtry文から制御が離れる際に常に実行される。正常実行の結果として,
break文,continue文,goto文若しくはreturn文の実行の結果として,又は例外をtry文の外側に
伝ぱ(播)した結果として,この制御の移行が行われる。
finallyブロックの実行中に例外が送出される場合,その例外は取り囲んでいる次のtry文に伝ぱ(播)
する。伝ぱ(播)中の処理に別の例外があった場合,その例外は失われる。例外を伝ぱ(播)する処理は
throw文(15.9.5参照)の規定で示す。
try文のtryブロックは,そのtry文が到達可能な場合,到達可能とする。
try文のcatchブロックは,そのtry文が到達可能な場合,到達可能とする。
try文のfinallyブロックは,そのtry文が到達可能な場合,到達可能とする。
try文の終了点は次の両方が真の場合,到達可能とする。
− tryブロックの終了点が到達可能,又は少なくとも一つのcatchブロックが到達可能。
− finallyブロックが存在し,そのfinallyブロックの終了点が到達可能。
15.11
checked文及びunchecked文
checked文及びunchecked文は整数型の算術演算と変換に対するけた(桁)あふれ検査文脈を制御す
るために使われる。
≪checked文≫:
checked ≪ブロック≫
≪unchecked文≫:
unchecked ≪ブロック≫
checked文は≪ブロック≫中のすべての式が検査文脈内で評価されるようにする。また,unchecked
267
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
文は≪ブロック≫内のすべての式が非検査文脈内で評価されるようにする。
checked文及びunchecked文は,それらが式の代わりにブロックに作用することを除き,checked
演算子及びunchecked演算子(14.5.13参照)と厳密に同等とする。
15.12
lock文
lock文は指定されたオブジェクトに対する相互排他ロックを取得し,文を実行し,取得したロックを
解放する。
≪lock文≫:
lock ( ≪式≫ ) ≪埋込み文≫
lock文の式のコンパイル時の型は≪参照型≫又は参照型になることが分かっている型仮引数(25.1.1
参照)でなければならない。式のコンパイル時の型として,≪値型≫を指定するとコンパイル時エラーに
なる。
lock (x) …
この形式のlock文は,次のコードと厳密に同等とする。
object obj = x;
System.Threading.Monitor.Enter(obj);
try {
…
}
finally {
System.Threading.Monitor.Exit(obj);
}
例
class Cache
{
public void Add(object x) {
lock (key) {
…
}
}
public void Remove(object x) {
lock (key) {
…
}
}
private readonly object key = new object();
}
15.13
using文
using文は1個以上の資源を取得し,文を実行し,取得した資源を廃棄する。
≪using文≫:
using ( ≪資源取得子≫ ) ≪埋込み文≫
268
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪資源取得子≫:
≪局所変数宣言≫
≪式≫
資源とは,System.IDisposableインタフェースを実装したクラス又は構造体とする。
System.IDisposableインタフェースは,仮引数をもたないDisposeという名前のメソッドを一つ含
む。資源を使用するコードは,その資源がもはや必要でないということを示すためにDisposeを呼び出
すことができる。Disposeが呼び出されない場合は,ゴミ集めの結果として最終的には自動的に廃棄され
る(資源型の終了化子はDisposeを呼び出すか,又は,それと同等の動作を実行することを想定してい
る)。
≪資源取得子≫で宣言された局所変数は,読込み専用であり,初期化子を含まなければならない。埋込
み文がこれらの局所変数を(代入,演算子++又は演算子--を使って)変更しようとした場合,又はこれら
の局所変数をref仮引数若しくはout仮引数として渡そうとした場合,コンパイル時エラーになる。
using文は,取得,使用,廃棄の三つの部分に変換される。資源の使用は暗黙にfinally節を含むtry
文に取り囲まれる。このfinally節は資源を廃棄する。資源としてnullを取得した場合,Disposeの
呼出しは行われず,例外が送出される。
using (≪式≫) ≪埋込み文≫
上記の形式のusing文は次の形式のusing文と同等とする。
using (ResourceType resource = ≪式≫) ≪埋込み文≫
ここで,ResourceTypeは≪式≫の型,変数resourceはプログラムの他のいかなるソースコードか
らも不可視,かつ,アクセス不可能とする。この形式のusing文は,次の展開されたコードと同等とする。
{
ResourceType resource = ≪式≫;
try {
≪埋込み文≫;
}
finally {
... // Dispose of resource
}
}
変数resourceは,埋込み文の中で読込み専用とする。
finallyブロックの厳密な形式は,次のように決定される。
− ResourceTypeからSystem.IDisposableインタフェースへの暗黙の変換が存在しない場合,コ
ンパイル時エラーとする。
− そうでなく,ResourceTypeがnull許容でない値型又は値型制約(25.7参照)付きの型仮引数の場
合,finally節は次のコードと意味論的に同等のものに展開される。
finally {
((System.IDisposable)resource).Dispose();
}
ただし,resourceからSystem.IDisposableへのキャストで,本来起こるべきボックス化が生
じない点は異なる。
269
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− そうでなければ,finally節は次のように展開される。
finally {
if (resource != null) ((System.IDisposable)resource).Dispose();
}
≪資源取得子≫が≪局所変数宣言≫の形式をとる場合は,指定された型の複数の資源を取得できる。
using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) statement
この形式のusing文は,次の入れ子になったusing文の列と厳密に同等とする。
using (ResourceType r1 = e1)
using (ResourceType r2 = e2)
...
using (ResourceType rN = eN)
statement
例 次の例はlog.txtという名前のファイルを作成し,そのファイルにテキストの2行を書き込む。
次に,読込み用に同じファイルを開き,ファイルに含まれているテキスト行を端末にコピーする。
using System;
using System.IO;
class Test
{
static void Main() {
using (TextWriter w = File.CreateText("log.txt")) {
w.WriteLine("This is line one");
w.WriteLine("This is line two");
}
using (TextReader r = File.OpenText("log.txt")) {
string s;
while ((s = r.ReadLine()) != null) {
Console.WriteLine(s);
}
}
}
}
TextWriter及びTextReaderクラスはIDisposableインタフェースを実装するので,こ
の例では,次の読込み操作又は書込み操作の前に,前提となっているファイルが適切に閉じられ
ることを保証するためにusing文を用いることができる。
15.14
yield文
yield文は反復子ブロック(26.1参照)内で列挙子オブジェクトに値を与えるため,又は,反復の終了
を通知するために使用する。
≪yield文≫ :
yield return ≪式≫ ;
yield break ;
270
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
既存のプログラムとの互換性を確保するため,yieldはキーワードとはしない。yieldは,キーワード
return又はキーワードbreakの直前で使われた場合にだけ,特別な意味をもつものとする。それ以外の
文脈では,yieldを識別子として使うことができる。
yield文を記述できる場所には,次に説明するとおり,幾つかの制限がある。
− (いずれの形式についても)yield文が,≪メソッド本体≫の外側,≪演算子本体≫の外側又は≪ア
クセス子本体≫の外側に現れた場合,コンパイル時エラーとする。
− (いずれの形式についても)yield文が無名メソッドの内部に現れた場合,コンパイル時エラーとす
る。
− (いずれの形式についても)yield文が,try文のfinally節内に現れた場合,コンパイル時エラ
ーとする。
− yield return文が,≪catch節群≫を含むtry文の中のどこかに現れた場合,コンパイル時エラ
ーとする。
例 次に有効なyield文及び無効なyield文の使用例を示す。
delegate IEnumerable<int> D();
IEnumerator<int> GetEnumerator() {
try {
yield return 1;
// Ok
yield break;
// Ok
}
finally {
yield return 2;
// Error, yield in finally
yield break;
// Error, yield in finally
}
try {
yield return 3;
// Error, yield return in try...catch
yield break;
// Ok
}
catch {
yield return 4;
// Error, yield return in try...catch
yield break;
// Ok
}
D d = delegate {
yield return 5;
// Error, yield in an anonymous method
};
}
int MyMethod() {
yield return 1;
// Error, wrong return type for an
iterator
}
yield return文の中の式の型から反復子ブロックの産出型(26.1.3参照)への暗黙の変換(13.1参照)
271
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
が存在する。
yield return文は次のように実行される。
− 文中に与えられた式を評価し,産出型に暗黙の変換を行った後,列挙子オブジェクトのCurrent特
性に代入する。
− 反復子ブロックの実行を一時的に中断する。そのyield return文が1個以上のtryブロックの内
側にある場合,関連するfinallyブロックはこの時点では実行しない。
− 列挙子オブジェクトのMoveNextメソッドは,列挙子オブジェクトが次の項目に正常に進むことがで
きたことを示すために,呼出し側にtrueを返す。
列挙子オブジェクトのMoveNextメソッドの次の呼出しは,反復子ブロックが最後に実行を中断した場
所,すなわち,中断したyield return文の終了点から実行を再開する。
yield break文は次のように実行される。
− そのyield break文が関連するfinallyブロックをもつ1個以上のtryブロックに囲まれている
場合,まず最も内側のtry文のfinallyブロックに制御を移す。制御がfinallyブロックの終了
点に到達した場合,次の取り囲むtry文のfinallyブロックに制御を移す。すべての取り囲むtry
文のfinallyブロックを実行し終えるまで,この処理を繰り返す。
− 反復子ブロックの呼出し側に制御を戻す。反復子の呼出し側とは,その列挙子オブジェクトの
MoveNextメソッド又はDisposeメソッドのいずれかとする。
yield break文は無条件に他の場所に制御を移すため,yield break文の終了点は決して到達可能に
ならない。
16 名前空間
C#プログラムは,名前空間を用いて編成される。名前空間は,プログラムに対する“内部的な”編成体
系及び“外部的な”編成体系の両方として使用される。ここで,外部的な編成体系とは,他のプログラム
に公開するプログラム構成要素を提供する方法を指す。
名前空間の利用を可能にするためにusing指令(16.4参照)を提供する。
16.1 コンパイル単位
≪コンパイル単位≫は,ソースファイルの構造全体を定義する。コンパイル単位は,順に,0個以上の
≪外部別名指令群≫,0個以上の≪using指令群≫,0個以上の≪大域的属性群≫,0個以上の≪名前空間
メンバ宣言群≫で構成される。
≪コンパイル単位≫:
≪外部別名指令群≫opt ≪using指令群≫opt ≪大域的属性群≫opt ≪名前空間メ
ンバ宣言群≫opt
C#プログラムは,一つ以上のコンパイル単位で構成される。各コンパイル単位は別々のソースファイル
に含まれる。C#プログラムがコンパイルされる際には,すべてのコンパイル単位が同時に処理される。し
たがって,コンパイル単位は相互に依存していてもよい。循環状の依存も可能とする。
コンパイル単位の≪外部別名指令群≫は,そのコンパイル単位の≪using指令群≫,≪大域的属性群≫
及び≪名前空間メンバ宣言群≫に影響を与えるが,他のコンパイル単位には影響を与えない。
コンパイル単位の≪using指令群≫は,そのコンパイル単位の≪大域的属性群≫及び≪名前空間メンバ
宣言群≫に影響を与えるが,他のコンパイル単位には影響を与えない。
コンパイル単位の≪大域的属性群≫(箇条24参照)によって,目的のアセンブリの属性を指定できる。
272
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
アセンブリは型に対しては物理的なコンテナとして振る舞う。
プログラムの各々のコンパイル単位の≪名前空間メンバ宣言群≫は,大域的名前空間と呼ばれる単一の
宣言空間にメンバを追加する。
例
File A.cs:
class A {}
File B.cs:
class B {}
この二つのコンパイル単位は単一の大域的名前空間に寄与する。この例では,完全限定名A及
びBで二つのクラスを宣言する。この二つのコンパイル単位は同じ宣言空間に寄与するので,各々
のコンパイル単位が同じ名前でメンバの宣言を行っている場合,(コンパイル時)エラーになる。
16.2 名前空間宣言
≪名前空間宣言≫は,キーワードnamespace,名前空間名及び名前空間本体,省略可能なセミコロン
で構成される。
≪名前空間宣言≫:
namespace ≪限定付き識別子≫ ≪名前空間本体≫ ;opt
≪限定付き識別子≫:
≪識別子≫
≪限定付き識別子≫ . ≪識別子≫
≪名前空間本体≫:
{ ≪外部別名指令群≫opt ≪using指令群≫opt
≪名前空間メンバ宣言群≫opt }
≪名前空間宣言≫は,≪コンパイル単位≫の最上位の宣言として記述でき,別の≪名前空間宣言≫内の
メンバ宣言として記述できる。≪名前空間宣言≫を≪コンパイル単位≫の最上位の宣言として記述した場
合,その名前空間は大域的名前空間のメンバになる。≪名前空間宣言≫を別の≪名前空間宣言≫内に記述
した場合,内側の名前空間は外側の名前空間のメンバになる。いずれの場合でも,名前空間の名前は,記
述された名前空間内で一意でなければならない。
名前空間は暗黙にpublicとする。名前空間の宣言にはアクセス修飾子を含むことはできない。
≪名前空間本体≫内では,省略可能な≪using指令≫によって他の名前空間及び型の名前を移入する。
これによって,限定名を使わなくても直接参照できるようになる。省略可能な≪名前空間メンバ宣言≫は,
名前空間の宣言空間にメンバを追加する。≪外部別名指令群≫はすべて≪using指令群≫の前に記述しな
ければならず,≪外部別名指令群≫及び≪using指令≫はすべてメンバ宣言の前に記述しなければならな
い。
≪名前空間宣言≫の≪限定付き識別子≫は一つの識別子であってもよいし,字句“.”で区切られた識
別子の列であってもよい。後者の形式を使うことで,字句的に入れ子になった名前空間宣言を行わなくて
も,プログラムに入れ子になった名前空間を定義できるようになる。
例1
namespace N1.N2
{
class A {}
273
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class B {}
}
これは,次と同じ意味になる。
namespace N1
{
namespace N2
{
class A {}
class B {}
}
}
名前空間は,いつでも拡張可能であり,同じ完全限定名(10.8.2参照)をもつ二つの名前空間宣言は同
じ宣言空間に寄与する(10.3参照)。
例2
namespace N1.N2
{
class A {}
}
namespace N1.N2
{
class B {}
}
この例に示した二つの名前空間宣言は同じ宣言空間に寄与する。この例では完全限定名
N1.N2.A及びN1.N2.Bの二つのクラスを宣言している。この二つの宣言は同じ宣言空間に寄
与するので,各々の宣言が同じ名前のメンバの宣言を含んでいる場合には(コンパイル時)エ
ラーになる。
Systemで始まる名前の名前空間は標準ライブラリ(附属書D参照)で使用するために予約されている。
16.3 外部別名指令
≪外部別名指令≫は,外部に定義された名前空間に対する別名として使える識別子を導入する。別名を
与えられた名前空間の記述は,プログラムのソースコードの外部に存在する。
≪外部別名指令群≫:
≪外部別名指令≫
≪外部別名指令群≫ ≪外部別名指令≫
≪外部別名指令≫:
extern alias ≪識別子≫ ;
≪外部別名指令≫の有効範囲は,その≪外部別名指令≫を直接含んでいる≪コンパイル単位≫又は≪名
前空間本体≫の≪using指令群≫,≪大域的属性群≫及び≪名前空間メンバ宣言群≫にも及ぶ。≪外部別
名指令≫は,その≪外部別名指令≫を含んでいるコンパイル単位又は名前空間本体(10.3参照)の別名宣
言空間にその名前を寄与するが,その≪外部別名指令≫を含んでいる名前空間の宣言空間には寄与しない。
≪外部別名指令≫を含むコンパイル単位又は名前空間本体の内部で,別名を付けられた名前空間を参照
274
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
するために,その≪外部別名指令≫が導入した識別子を使うことができる。≪識別子≫がglobalという
語であった場合,コンパイル時エラーとする。
C#ソースコード内では,型は一つの名前空間のメンバとして宣言される。しかし,外部別名によって参
照される名前空間階層は,他の名前空間のメンバの型も含んでいてもよい。例えば,A及びBを外部別名
とすると,特定のコンパイラによってサポートされる外部別名の記述方法によっては,名前A::X,B::C.Y
及びglobal::D.Zのすべてが同じ型を参照していてもよい。
≪外部別名指令≫によって導入される別名は,≪using別名指令≫によって導入される別名とよく似て
いる。≪外部別名指令≫及び≪using別名指令≫のより詳細な議論については16.4.1に示す。
特性アクセス子の中のget及びsetと同様に,aliasはキーワード(9.4.3参照)ではない。aliasと
いう語は,≪外部別名指令≫中のキーワードexternの直後に続く場合にだけ,特別な意味をもつ。
例 実際,外部別名は,識別子aliasをその名前として使うことができる。
extern alias alias;
16.4 using指令
using指令は他の名前空間で定義されている名前空間及び型の利用を可能にする。≪using指令群≫は
≪名前空間名又は型名≫(10.8参照)及び≪単純名≫(14.5.2参照)の名前解決処理に影響を与えるが,
宣言とは異なり,≪using指令≫が,その≪using指令≫が使われているコンパイル単位又は名前空間の
基礎となる宣言空間内に新しいメンバを追加することはない。
≪using指令群≫:
≪using指令≫
≪using指令群≫ ≪using指令≫
≪using指令≫:
≪using別名指令≫
≪using名前空間指令≫
≪using別名指令≫(16.4.1参照)は名前空間又は型に対する別名を導入する。
≪using名前空間指令≫(16.4.2参照)は名前空間の型メンバを移入する。
≪using指令≫の有効範囲は,その≪using指令≫を直接含んでいるコンパイル単位又は名前空間本体
の≪名前空間メンバ宣言群≫にまで及ぶ。≪using指令≫の有効範囲は,同列の≪using指令≫を含まな
い。したがって,同列の≪using指令≫が相互に影響することはなく,using指令を記述する順序は重要
ではない。これに対して,≪外部別名指令≫の有効範囲は,同じコンパイル単位又は名前空間本体の中で
定義された≪using指令群≫を含む。
16.4.1 using別名指令
≪using別名指令≫は,すぐ外側のコンパイル単位又は名前空間本体内で,名前空間又は型の別名とな
る識別子を導入する。
≪using別名指令≫:
using ≪識別子≫ = ≪名前空間名又は型名≫ ;
≪using別名指令≫を含むコンパイル単位又は名前空間本体内の大域的属性及びメンバ宣言では,≪
using別名指令≫で導入された識別子を使って,特定の名前空間又は型を参照できる。
例
namespace N1.N2
{
275
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class A {}
}
namespace N3
{
using A = N1.N2.A;
class B: A {}
}
この例において,名前空間N3のメンバ宣言内で,AはN1.N2.Aの別名とするので,クラス
N3.BはクラスN1.N2.Aから派生する。次のように,N1.N2に対する別名Rを生成し,R.Aを
参照することによっても同様の効果を得ることができる。
namespace N3
{
using R = N1.N2;
class B: R.A {}
}
≪using別名指令≫を含むコンパイル単位又は名前空間本体の中のusing指令,大域的属性及びメン
バ宣言の内部では,≪外部別名指令≫によって導入された識別子を関連する名前空間を参照するために使
用することができる。
例
namespace N1
{
extern alias N2;
class B: N2::A {}
}
この例において,名前空間N1の中のメンバ宣言の範囲内で,N2は定義がこのプログラムのソ
ースコードの外部にある,いずれかの名前空間に対する別名となっている。クラスN1.Bはクラ
スN2.Aから派生している。次のようにN2.Aに対する別名Aを作り,Aを参照することによっ
て,同様の効果を得ることができる。
namespace N1
{
extern alias N2;
using A = N2::A;
class B: A {}
}
≪外部別名指令≫及び≪using別名指令≫は,特定のコンパイル単位又は名前空間本体内で別名を使用
できるようにするが,基礎とする宣言空間に新規メンバを追加するわけではない。すなわち,別名指令は
透過的ではなく,その別名指令が記述されているコンパイル単位又は名前空間本体の中でだけ有効とする。
例
namespace N3
{
276
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
extern alias R1;
using R2 = N1.N2;
}
namespace N3
{
class B: R1::A, R2.I {}
// Error, R1 and R2 unknown
}
R1及びR2を導入する別名指令の有効範囲は,R1及びR2が記述されている名前空間本体のメ
ンバ宣言内にしか及ばないので,2番目の名前空間宣言ではR1及びR2は認識されない。ただし,
次のようにこれらの名前空間宣言を含んでいるコンパイル単位に別名指令を配置すると,別名は
両方の名前空間宣言内で利用できるようになる。
extern alias R1;
using R2 = N1.N2;
namespace N3
{
class B: R1::A, R2.I {}
}
namespace N3
{
class C: R1::A, R2.I {}
}
≪コンパイル単位≫又は≪名前空間本体≫の中のそれぞれの≪外部別名指令≫又は≪using別名指令
≫は,その≪外部別名指令≫又は≪using別名指令≫を直接取り囲む≪コンパイル単位≫又は≪名前空間
本体≫の別名宣言空間(10.3参照)に名前を追加する。別名指令の≪識別子≫は,対応する別名宣言空間
の中で一意になっていなければならない。この識別子は,大局的宣言空間,又は対応する名前空間の宣言
空間の内部で一意になっている必要はない。
例
extern alias A;
extern alias B;
using A = N1.N2;
// Error: alias A already exists
class B {}
// Ok
Aという名前を付けようとしているusing別名は,同じコンパイル単位内に既にAという名前
の別名が存在しているため,エラーを起こす。Bと名付けられたクラスとBと名付けられた外部
別名は,別々の宣言空間に追加されるため衝突しない。前者は大域的宣言空間に追加され,後者
はこのコンパイル単位の別名宣言空間に追加される。
別名が名前空間のメンバの名前と一致している場合,次のようにいずれも適切に限定して使用
しなければならない。
namespace N1.N2
{
class B {}
277
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
namespace N3
{
class A {}
class B : A {}
}
namespace N3
{
using A = N1.N2;
using B = N1.N2.B;
class W : B {}
// Error: B is ambiguous
class X : A.B {}
// Error: A is ambiguous
class Y : A::B {}
// Ok: uses N1.N2.B
class Z : N3.B {}
// Ok: uses N3.B
}
N3についての2番目の名前空間本体の中で,N3がBという名前のメンバ及びBという名前で
別名を宣言する名前空間本体を含んでいるため,Bを限定せずに使用するとエラーになる。Aに
ついても同様とする。クラスN3.BはN3.B又はglobal::N3.Bとして参照できる。別名Aは
≪限定別名メンバ≫(16.7参照)の中で,A::Bのように使うことができる。別名Bは基本的に
は役に立たない。別名Bは,名前空間の別名を≪限定別名メンバ≫の中で使うことができ,Bは
型の別名になるだけなので,≪限定別名メンバ≫の中では使うことができない。
別名指令で導入された名前は,通常のメンバと同様に,入れ子になった有効範囲内の同じ名前のメンバ
によって隠ぺいされる。
例
using R = N1.N2;
namespace N3
{
class R {}
class B: R.A {}
// Error, R has no member A
}
この例では,RはN1.N2ではなくN3.Rを参照するので,Bの宣言中のR.Aの参照はコンパイ
ル時エラーになる。
≪外部別名指令≫の記述順序に意味はない。同様に,≪using別名指令≫の記述順序に意味はないが,
すべての≪using別名指令≫は,同じ翻訳単位又は名前空間本体内のすべての≪外部別名指令≫の後に続
かなければならない。≪using別名指令≫によって参照される≪名前空間名又は型名≫の解決は,≪
using別名指令≫自体にも,その≪名前空間名又は型名≫を直接含んでいるコンパイル単位又は名前空間
本体に含まれる他の≪using指令≫にも影響されないが,その≪名前空間名又は型名≫を直接含んでいる
コンパイル単位又は名前空間本体の中の≪外部別名指令群≫の影響を受けることがある。すなわち,≪
using別名指令≫の≪名前空間名又は型名≫は,すぐ外側のコンパイル単位又は名前空間本体に≪using
指令≫がなく,≪外部別名指令≫があるかのように解決される。
278
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例
namespace N1.N2 {}
namespace N3
{
extern alias E;
using R1 = E::N;
// OK
using R2 = N1;
// OK
using R3 = N1.N2;
// OK
using R4 = R2.N2;
// Error, R2 unknown
}
前の≪using別名指令≫の影響が及ばないので,最後の≪using別名指令≫はコンパイル時エ
ラーになる。最初の≪using別名指令≫は,外部別名Eの有効範囲がこの≪using別名指令≫
を含んでいるので,エラーにならない。
≪using別名指令≫は,任意の名前空間又は型の別名を生成できる。別名が生成できる名前空間及び型
には,その≪using別名指令≫が記述されている名前空間,及び,その名前空間内の入れ子になった名前
空間又は型も含まれる。
別名を使って名前空間又は型にアクセスした結果は,宣言名を使ってその名前空間又は型にアクセスし
た結果と全く同じになる。
例
namespace N1.N2
{
class A {}
}
namespace N3
{
using R1 = N1;
using R2 = N1.N2;
class B
{
N1.N2.A a;
// refers to N1.N2.A
R1.N2.A b;
// refers to N1.N2.A
R2.A c;
// refers to N1.N2.A
}
}
名前N1.N2.A,R1.N2.A及びR2.Aは等価であり,いずれも完全限定名がN1.N2.Aのクラス
を参照する。
部分型(17.1.4参照)の各部分が同一の名前空間内に宣言されている場合であっても,通常,その部分
は異なる名前空間宣言群の中に記述される。したがって,各部分に対して,異なるextern alias指令群
及びusing指令群が存在することがある。一つの部分の中の単純名(14.5.2参照)を解釈する場合,その
部分を取り囲む名前空間本体群並びにコンパイル単位のextern alias指令及びusing指令だけが考慮
279
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
される。その結果が,異なる部分について異なる意味をもつ同一の識別子になってもよい。
例
namespace N
{
using List = System.Collections.ArrayList;
partial class A
{
List x;
// x has type
System.Collections.ArrayList
}
}
namespace N
{
using List = Widgets.LinkedList;
partial class A
{
List y;
// y has type Widgets.LinkedList
}
}
16.4.2 using名前空間指令
≪using名前空間指令≫は,名前空間に含まれる型を,その≪using名前空間指令≫を直接取り囲んで
いるコンパイル単位又は名前空間の本体に移入する。これによって,各型の識別子を限定なしで使用でき
る。
≪using名前空間指令≫:
using ≪名前空間名≫ ;
≪using名前空間指令≫を含むコンパイル単位又は名前空間本体のメンバ宣言内では,指定された名前
空間に含まれる型を直接参照できる。
例
namespace N1.N2
{
class A {}
}
namespace N3
{
using N1.N2;
class B: A {}
}
上の例において,名前空間N3内のメンバ宣言内では,N1.N2の型メンバは直接利用可能なの
で,クラスN3.BはクラスN1.N2.Aから派生する。
≪using名前空間指令≫は,指定された名前空間に含まれる型を移入できるが,その名前空間内に入れ
280
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
子になっている名前空間までは移入しない。
例
namespace N1.N2
{
class A {}
}
namespace N3
{
using N1;
class B: N2.A {}
// Error, N2 unknown
}
≪using名前空間指令≫はN1に含まれる型を移入するが,N1内の入れ子になった名前空間ま
では移入しない。したがって,名前空間N2内のメンバが有効範囲内にないので,Bの宣言内で
使われているN2.Aへの参照はコンパイル時エラーになる。
≪using別名指令≫とは異なり,≪using名前空間指令≫は,その≪using名前空間指令≫を取り囲
んでいるコンパイル単位又は名前空間本体の中で識別子が定義済みになっている型を移入できる。実際に
は,≪using名前空間指令≫が移入した名前は,その≪using名前空間指令≫を取り囲んでいるコンパイ
ル単位又は名前空間本体内の同じ名前のメンバによって隠ぺいされる。
例
namespace N1.N2
{
class A {}
class B {}
}
namespace N3
{
using N1.N2;
class A {}
}
この例において,名前空間N3内のメンバ宣言内では,AはN1.N2.AではなくN3.Aを参照す
る。
同じコンパイル単位又は名前空間本体内の≪using名前空間指令≫が移入した複数の名前空間が同名
の型を含んでいる場合,その名前に対する参照はあいまいであるとみなされる。
例
namespace N1
{
class A {}
}
namespace N2
{
281
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class A {}
}
namespace N3
{
using N1;
using N2;
class B: A {}
// Error, A is ambiguous
}
N1及びN2の両方がメンバAを含み,N3がN1及びN2の両方を移入するので,N3内のAに
対する参照はコンパイル時エラーになる。この状況では,名前の衝突を,Aに対する参照を限定
するか,いずれか一つのAを特定する≪using別名指令≫を導入することによって解決できる。
namespace N3
{
using N1;
using N2;
using A = N1.A;
class B: A {}
// A means N1.A
}
≪using別名指令≫と同様に,≪using名前空間指令≫は,名前空間本体の基礎となる宣言空間に新規
メンバを追加するのではなく,その≪using名前空間指令≫が記述されているコンパイル単位又は名前空
間本体内だけに影響を与える。
≪using名前空間指令≫によって参照される≪名前空間名≫は,≪using別名指令≫によって参照され
る≪名前空間名又は型名≫と同じ方法で解決される。したがって,同じコンパイル単位又は名前空間本体
に含まれる複数の≪using名前空間指令≫は,相互に影響せず,任意の順序で記述できる。
16.5 名前空間メンバ
≪名前空間メンバ宣言≫は,≪名前空間宣言≫(16.2参照)又は≪型宣言≫(16.6参照)のいずれかと
なる。
≪名前空間メンバ宣言群≫:
≪名前空間メンバ宣言≫
≪名前空間メンバ宣言群≫ ≪名前空間メンバ宣言≫
≪名前空間メンバ宣言≫:
≪名前空間宣言≫
≪型宣言≫
コンパイル単位又は名前空間本体は,≪名前空間メンバ宣言≫を含むことができる。その名前空間メン
バの宣言は,その≪名前空間メンバ宣言≫が記述されているコンパイル単位又は名前空間本体の基礎とな
る宣言空間に,新規メンバを追加する。
16.6 型宣言
≪型宣言≫は,≪クラス宣言≫(17.1参照),構造体宣言(18.1参照),≪インタフェース宣言≫(20.1
参照),≪列挙宣言≫(21.1参照)又は≪委譲宣言≫(22.1参照)のいずれかとなる。
≪型宣言≫:
282
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪クラス宣言≫
≪構造体宣言≫
≪インタフェース宣言≫
≪列挙宣言≫
≪委譲宣言≫
≪型宣言≫は,コンパイル単位の最上位の宣言として,又は,名前空間,クラス若しくは構造体内のメ
ンバ宣言として記述できる。
型Tの型宣言をコンパイル単位の最上位の宣言として記述した場合,この型宣言の完全限定名(10.8.2
参照)はその宣言の非限定名(10.8.1参照)と同じものとする。型Tの型宣言を,名前空間,クラス又は
構造体内に記述した場合,その型宣言の完全限定名(10.8.2参照)はS.Nとなる。ここで,Sは型Tの型
宣言を含んでいる名前空間又は型宣言の完全限定名とし,Nはその宣言の非限定名(10.8.1参照)とする。
クラス又は構造体内で宣言した型を,入れ子型(17.2.6参照)と呼ぶ。
型宣言に対して指定できるアクセス修飾子及び省略時のアクセス修飾子は,次のように,その宣言が記
述された位置の文脈に依存する(10.5.1参照)。
− コンパイル単位又は名前空間宣言内で宣言された型にはpublic又はinternalアクセスを指定でき
る。省略時はinternalアクセスとする。
− クラス内で宣言する型には,public,protected internal,protected,internal又はprivate
のアクセス修飾子を指定できる。省略時はprivateとする。
− 構造体内で宣言する型には,public,internal又はprivateのアクセス修飾子が指定できる。省
略時はprivateとする。
16.7 限定別名メンバ
≪限定別名メンバ≫は,他の実体によって隠ぺいされている可能性のある大域的名前空間並びに外部別
名及びusing別名に対して明示的にアクセスする方法を提供する。
≪限定別名メンバ≫:
≪識別子≫ :: ≪識別子≫ ≪型実引数並び≫opt
≪限定別名メンバ≫は,≪名前空間名又は型名≫(10.8参照)又は≪メンバアクセス≫(14.5.4参照)
の左側の演算対象として使うことができる。
左側及び右側の識別子として参照される二つの識別子の≪限定別名メンバ≫は,字句::で区切られ,そ
の後ろに省略可能な≪型実引数並び≫が続く。左側の識別子がglobalの場合,その大域的名前空間で右
側の識別子を検索する。それ以外の任意の左側の識別子については,外部別名(16.3参照)又はusing
別名(16.4.1参照)として検索する。そのような別名が存在しないか,又は,その別名が型を参照してい
る場合,コンパイル時エラーとする。その別名が名前空間を参照している場合,その名前空間から右側の
識別子を探す。
≪限定別名メンバ≫は,次の二つのうちのいずれかの形式をもつ。
− A::B<G1, ..., GN>。ここで,A及びBは識別子を表し,<G1, ..., GN>は型実引数並び(Nは常に少なく
とも1以上)とする。
− A::B。ここで,A及びBは識別子を表す(この場合,Nはゼロとみなされる)。
この記法を使う場合,≪限定別名メンバ≫の意味は次のように決定する。
− Aが識別子globalの場合,次のように大域的名前空間からBを探す。
・ 大域的名前空間がBという名前の名前空間を含んでいて,かつ,Nがゼロの場合,≪限定別名メン
283
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
バ≫はその名前空間を参照する。
・ そうでなく,大域的名前空間がBという名前の非総称型を含んでいて,かつ,Nがゼロの場合,≪
限定別名メンバ≫はその型を参照する。
・ そうでなく,大域的名前空間がN個の型仮引数をもつBという名前の型を含んでいる場合,≪限定
別名メンバ≫は,与えられた型実引数で構築されたその型を参照する。
・ そうでなければ,≪限定別名メンバ≫は未定義とし,コンパイル時エラーとする。
− そうでなければ,(もし存在すれば)≪限定別名メンバ≫を直接含む名前空間宣言(16.2参照)から始
め,(もし存在すれば)さらにそれを取り囲んでいる名前空間宣言のそれぞれについて評価を継続し,
その≪限定別名メンバ≫を含んでいるコンパイル単位で終わるまでを,実体が見つかるまで次のよう
に評価する。
・ 名前空間宣言又はコンパイル単位が,Aを型と関連付ける≪using別名指令≫を含んでいる場合,
≪限定別名メンバ≫は未定義とし,コンパイル時エラーとする。
・ そうでなく,名前空間宣言又はコンパイル単位がAを名前空間と関連付ける≪外部別名指令≫又は
≪using別名指令≫を含んでいる場合,次のように評価する。
− Aと関連付けられた名前空間がBという名前の名前空間を含んでいて,かつ,Nがゼロの場合,
≪限定別名メンバ≫はその名前空間を参照する。
− そうでなく,Aと関連付けられた名前空間がBという名前の非総称型を含んでいて,かつ,Nが
ゼロの場合,≪限定別名メンバ≫はその型を参照する。
− そうでなく,Aと関連付けられた名前空間がN個の型仮引数をもつBという名前の名前空間を含
んでいる場合,≪限定別名メンバ≫は,与えられた型実引数で構築されたその型を参照する。
− そうでなければ,≪限定別名メンバ≫は未定義とし,コンパイル時エラーが起こる。
− そうでなければ,≪限定別名メンバ≫は未定義とし,コンパイル時エラーとする。
例
using S = System.Net.Sockets;
class A {
public static int x;
}
class C {
public void F(int A, object S) {
// Use global::A.x instead of A.x
global::A.x += A;
// Use S::Socket instead of S.Socket
S::Socket s = S as S::Socket;
}
}
上記のコード例において,クラスAはglobal::Aと参照されており,型
System.Net.Sockets.SocketはS::Socketと参照されている。それぞれglobal::A及び
System.Net.Sockets.Socketと参照する代わりにA.x及びS.Socketを使用すると,A及びSが仮
引数として解決されるため,コンパイル時エラーが起こる。
注記 識別子globalは,≪限定別名メンバ≫の左側の識別子として使われた場合にだけ,特別な意
284
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
味をもつ。識別子globalは,キーワードとせず,かつ,それ自身を別名にはできないことと
する。
class A { }
class C {
global.A x; // Error: global is not defined
global::A y; // Valid: References A in the global namespace
}
上記のコード例において,global.Aという使い方は,有効範囲内にglobalという名前の実体がない
ためコンパイル時エラーを起こす。もし有効範囲内にglobalという名前の何らかの実体があったならば,
global.Aのglobalはその実体として解決されていたであろう。
≪限定別名メンバ≫の左側の識別子としてのglobalを使用すると,たとえglobalという名前の
using別名が存在していたとしても,常に大域的名前空間の検索が行われる。
例
using global = MyGlobalTypes;
class A { }
class C {
global.A x; // Valid: References MyGlobalTypes.A
global::A y; // Valid: References A in the global namespace
}
上記のコードにおいて,global.AはMyGlobalTypes.Aに解決され,大域的名前空間の
global::AはクラスAとして解決される。
17 クラス
クラスは,データメンバ(定数及びフィールド),関数メンバ(メソッド,特性,イベント,添字子,演
算子,インスタンス構築子,終了化子及び静的構築子),及び,入れ子型を格納できるデータ構造とする。
クラス型は継承できる。継承は,派生クラスが基底クラスを拡張及び特化できる機構とする。
17.1 クラス宣言
≪クラス宣言≫は,新しいクラスを宣言する≪型宣言≫(16.6参照)とする。
≪クラス宣言≫:
≪属性群≫opt ≪クラス修飾子群≫opt partialopt class ≪識別子≫ ≪型仮
引数並び≫opt ≪クラス基底≫opt ≪型仮引数制約群節群≫opt ≪クラス本体≫ ;opt
≪クラス宣言≫は,省略可能な≪属性≫(箇条24参照)の集合,省略可能な≪クラス修飾子≫(17.1.1
参照)の集合,省略可能なpartial修飾子(17.1.4参照),キーワードclass及びそのクラスに名前を与
える≪識別子≫,省略可能な≪型仮引数並び≫(25.1.1参照),省略可能な≪クラス基底≫指定(17.1.2参
照),省略可能な≪型仮引数制約群節群≫(25.7参照),≪クラス本体≫(17.1.3参照)並びに省略可能な
セミコロンがこの順番で現れることによって構成される。
≪型仮引数並び≫が含まれないクラス宣言には,≪型仮引数制約群節群≫が含まれてはならない。
≪型仮引数並び≫を含むクラス宣言を,総称クラス宣言(25.1参照)とする。
17.1.1 クラス修飾子
≪クラス宣言≫は,省略可能なクラス修飾子の列を含んでもよい。
285
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪クラス修飾子群≫:
≪クラス修飾子≫
≪クラス修飾子群≫ ≪クラス修飾子≫
≪クラス修飾子≫:
new
public
protected
internal
private
abstract
sealed
static
一つのクラス宣言内で同じ修飾子が複数回現れた場合,コンパイル時エラーとする。
入れ子クラスに対して,new修飾子を指定できる。入れ子クラスに対するnew修飾子指定は,そのクラ
スが継承したメンバを同じ名前で隠ぺいすることを指定する。入れ子クラスに対するnew修飾子指定につ
いては17.2.2で規定する。new修飾子が入れ子クラスの宣言でないクラス宣言に現れた場合,コンパイル
時エラーとする。
public修飾子,protected修飾子,internal修飾子及びprivate修飾子は,そのクラスのアクセ
ス可能性を制御する。クラス宣言が行われている文脈に依存して,これらの修飾子の幾つかは許可されな
いことがある(10.5.1参照)。
部分型宣言(17.1.4参照)が(public修飾子,protected修飾子,internal修飾子及びprivate 修
飾子を介して)アクセス可能性指定を含む場合,同じ型に対する別の部分型宣言のすべてにおいて,アク
セス可能性指定が一致していなければならない。部分型宣言のすべてにアクセス可能性指定が含まれない
場合,その型には適切な省略時アクセス可能性(10.5.1参照)が与えられる。
abstract修飾子,sealed修飾子及びstatic修飾子は17.1.1.1〜17.1.1.3で規定する。
17.1.1.1 抽象クラス
abstract修飾子は,クラスが不完全であること,及びそのクラスが基底クラスとしてだけ使用される
ことを示すために使われる。抽象クラスと非抽象クラスとの違いは次のとおりとなる。
− 抽象クラスは直接具現化できない。抽象クラスにnew演算子を使用した場合,コンパイル時エラーと
する。コンパイル時の型が抽象である変数及び値をもつことはできるが,その抽象型の変数及び値は
null又はその抽象型から派生した非抽象型のインスタンスへの参照でなければならない。
− 抽象クラスは,抽象メンバを含んでもよいが,必すではない。
− 抽象クラスは封印クラスにできない。
非抽象クラスが抽象クラスから派生する場合,その非抽象クラスは,継承したすべての抽象メンバの実
際の実装を含む必要がある。このような実装は,継承した抽象メンバを上書きする。
例 次に例を示す。
abstract class A
{
public abstract void F();
}
286
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
abstract class B: A
{
public void G() {}
}
class C: B
{
public override void F() {
// actual implementation of F
}
}
抽象クラスAは抽象メソッドFを導入する。クラスBは追加のメソッドGを導入するが,Fの
実装を行っていないので,クラスBも抽象クラスとして宣言されなければならない。クラスCは
Fを上書きし,実際に実装を提供している。Cには抽象メンバが存在しないので,Cは非抽象ク
ラスとして宣言してもよいが,必すではない。
部分型宣言(17.1.4参照)の一つ以上の部分でabstract修飾子を含んでいる場合,そのクラスを抽象
クラスとする。どの部分型宣言にもabstract修飾子が含まれていない場合,そのクラスを非抽象クラス
とする。
17.1.1.2 封印クラス
sealed修飾子はクラスの派生を抑止するために使用する。封印クラスが別のクラスの基底クラスとし
て指定された場合,コンパイル時エラーとする。
封印クラスは同時に抽象クラスにできない。
注記 sealed修飾子は意図しない派生を抑止するために主に使われるが,同時にある種の実行時最
適化を可能にする。特に,封印クラスは派生クラスを一切もたないことが分かっているので,
封印クラスのインスタンスに対する仮想関数メンバの呼出しを非仮想呼出しに変換できる。
部分型宣言(17.1.4参照)の一つ以上の部分でsealed修飾子を含んでいる場合,そのクラスを封印ク
ラスとする。どの部分型宣言にもsealed修飾子が含まれていない場合,そのクラスを非封印クラスとす
る。
17.1.1.3 静的クラス
クラス宣言にstatic修飾子が含まれる場合,宣言しているクラスを,静的クラスという。静的クラス
宣言には,次の制約が課せられる。
− 静的クラスは,sealed 修飾子又はabstract修飾子を含んではならない。しかし,静的クラスはイ
ンスタンスの生成及びクラスを派生させることができないので,封印クラス及び抽象クラスの両方で
あるかのように振る舞う。
− 静的クラスは≪クラス基底≫指定(17.1.2参照)を含めてはならず,基底クラス又は実装されたイン
タフェースの列を明示的に指定できない。静的クラスは暗黙に型objectを継承する。
− 静的クラスに演算子を格納してはならない。
− 静的クラスにprotected修飾子又はprotected internal修飾子で宣言されたアクセス可能性の
メンバをもってはならない。
− 静的クラスは静的メンバ(17.2.5参照)だけを格納できる。
注記 定数及び入れ子クラスは静的メンバとして分類される。
287
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例 次に例を示す。
using System.Diagnostics;
public static class precondition
{
public sealed class Exception : System.ApplicationException
{
public Exception(string message) : base(message) { }
}
[Conditional(“CHECK̲PRECONDITIONS”)]
public static void Required(bool condition, string description) {
if(!condition) {
throw new Exception(description);
}
}
}
静的クラスはインスタンス構築子をもたない。静的クラス内にインスタンス構築子を宣言できない。静
的クラスに対する省略時のインスタンス構築子(17.10.4参照)は供給されない。
静的クラスのメンバは,暗黙的に静的にしない。定数及び入れ子型を除いて,静的であることを意図し
たメンバ宣言には,明示的にstatic修飾子を含めなければならない。クラスが静的クラス内に入れ子に
されるとき,その入れ子クラスは,明示的にstatic修飾子を含めない限り静的クラスではない。
≪名前空間名又は型名≫(10.8参照)は,次のいずれかの場合に静的クラスを参照できる。
− ≪名前空間名又は型名≫が,T.Iという形式の≪名前空間又は型名≫内のTである
− ≪名前空間名又は型名≫が,typeof(T)という形式の≪typeof式≫内のTである
≪一次式≫(14.5参照)は,次の場合に静的クラスを参照できる。
− ≪一次式≫が,E.Iという形式の≪メンバアクセス≫内のEである
それ以外の文脈で,静的クラスを参照するとコンパイル時エラーとなる。
注記 例えば,静的クラスを基底クラス,メンバの成分要素型(17.2.4参照),総称型実引数又は型仮
引数制約として利用することはエラーとなる。同様に,静的クラスは配列型,ポインタ型,new
式,キャスト式,is式,as式,sizeof式又は省略時値式で利用することもできない。
部分型宣言(17.1.4参照)の一つ以上の部分でstatic修飾子を含んでいる場合,そのクラスを静的ク
ラスとする。どの部分型宣言にもstatic修飾子が含まれていない場合,そのクラスを非静的クラスとす
る。
17.1.2 クラス基底指定
クラスの宣言には,≪クラス基底≫指定を含めることができる。この指定では,そのクラスの直接基底
クラス,及びそのクラスによって実装されるインタフェース(箇条20参照)を定義する。
≪クラス基底≫:
: ≪クラス型≫
: ≪インタフェース型並び≫
: ≪クラス型≫ , ≪インタフェース型並び≫
≪インタフェース型並び≫:
288
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪インタフェース型≫
≪インタフェース型並び≫ , ≪インタフェース型≫
17.1.2.1 基底クラス
≪クラス型≫が≪クラス基底≫に含まれる場合,それは宣言されているクラスの直接基底クラスを指定
する。部分クラス以外のクラス宣言に≪クラス基底≫が指定されていない又は≪クラス基底≫にインタフ
ェース型の並びだけが指定されている場合,そのクラスの直接基底クラスはobjectとする。部分クラス
宣言に基底クラス指定が含まれている場合,その基底クラス指定は,基底クラス指定を含む部分クラスの
他のすべての宣言と同じ型を参照しなければならない。部分型宣言のすべてに基底クラス指定が含まれて
いなければ,基底クラスはobjectとする。クラスは,17.2.1で規定しているとおり,その直接基底クラ
スからメンバを継承する。
例 次に例を示す。
class A {}
class B: A {}
クラスAはBの直接基底クラスといい,BはAから派生しているという。Aは明示的に直接基
底クラスを指定していないので,その直接基底クラスは暗黙にobjectとなる。
クラス宣言で指定された基底クラスは,構築クラス型(25.5参照)にできる。基底クラスは,それ自身
型仮引数にはできないが,有効範囲内にある型仮引数を取り込むことができる。
例 次に例を示す。
class Extend<V> : V {} // Error, type parameter used as base class
class C<V> : Base<V[]> {} // OK
クラス型の直接基底クラス及び任意の型実引数は,少なくともそのクラス型と同じアクセス可能性でな
ければならない(10.5.4参照)。例えば,privateクラス又はinternalクラスからpublicクラスを派
生させた場合,コンパイル時エラーとする。
クラス型の直接基底クラスは,System.Array,System.Delegate,System.Enum又は
System.ValueTypeであってはならない。
クラスの基底クラスは,構築総称型で型仮引数を型実引数に置き換えた後での直接基底クラス及びその
基底クラスとする。すなわち,基底クラスの集合は,直接基底クラスに関係する推移的閉包である。
注記 上記の例では,Bの基底クラスはA及びobjectである。
objectクラスを除いて,すべてのクラスは厳密に一つ直接基底クラスをもつ。objectクラスは直接
基底クラスをもたず,他のすべてのクラスの最終的な基底クラスになる。
クラスBがクラスAから派生していて,AがBに依存している場合,コンパイル時エラーとする。クラ
スは,その直接基底クラス(もしあれば)に直接依存しており,直接入れ子になっているクラス(もしあ
れば)に直接依存している。この定義によって,あるクラスが依存するクラスの完全な集合は,直接依存
関係の推移的閉包となる。
例 次の例はエラーになる。
class A: B {}
class B: C {}
class C: A {}
なぜなら,それぞれのクラスが自分自身に対して循環依存しているからである。同様に,次の
例もコンパイル時エラーになる。
289
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class A: B.C {}
class B: A
{
public class C {}
}
なぜなら,AはB.C(直接基底クラス)に依存しており,B.CはB(直接的な囲みクラス)に
依存しており,更にBはAに循環的に依存しているからである。
クラスは,その中で入れ子にされたクラスには依存しない。
例 次に例を示す。
class A
{
class B: A {}
}
BはAに依存する。なぜなら,Aはその直接基底クラス及びその直接的な囲みクラスの両方で
あるからである。しかし,AはBに依存しない。なぜなら,BはAの基底クラスでも囲みクラス
でもないからである。すなわち,これは正しい例である。
sealedクラスから派生することはできない。
例 次に例を示す。
sealed class A {}
class B: A {}
// Error, cannot derive from a sealed class
この例では,クラスBはsealedクラスAから派生しようとしているのでコンパイル時エラー
になる。
17.1.2.2 インタフェース実装
≪クラス基底≫指定には,インタフェース型の並びを含んでもよい。この場合,そのクラスは指定のイ
ンタフェース型を実装しているという。
複数の部分で宣言された型に対するインタフェースの集合は,個々の部分で指定されたインタフェース
の和集合である。個々の部分では特定のインタフェースを一つしか指定できないが,複数の部分で同じ基
底インタフェースを指定できる。与えられたインタフェースのメンバの実装は一つでなければならない。
例 次に例を示す。
partial class C : IA, IB { ... }
partial class C : IC { ... }
partial class C : IA, IB { ... }
これは,クラスCの基底インタフェースの集合がIA,IB及びICである。
通常,個々の部分では,その部分で宣言したインタフェースの実装を提供する。しかし,それは必すで
はない。ある部分で,別の部分で宣言したインタフェースに対する実装を提供してもよい。
例 次に例を示す。
partial class X
{
int IComparable.CompareTo(object o) { ... }
}
290
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
partial class X : IComparable
{
...
}
クラス宣言で指定された基底インタフェースは,構築インタフェース型(25.5参照)にできる。基底イ
ンタフェースは,それ自身型仮引数にできないが,有効範囲内にある型仮引数を取り込むことはできる。
例 次に例は,クラスが構築型を実装及び拡張できることを示す。
class C<U, V> { }
intarface I1<V> { }
class D : C<string, int>, I1<string> { }
class E<T> : C<int, T>, I1<T> { }
インタフェース実装は20.4で規定する。
17.1.3 クラス本体
クラスの≪クラス本体≫は,そのクラスのメンバを定義する。
≪クラス本体≫:
{ ≪クラスメンバ宣言≫opt }
17.1.4 部分宣言
partial修飾子は,クラス,構造体,インタフェース型を複数の部分で定義するときに使われる。特性
アクセス子におけるget及びsetと同様,partialはキーワードではない。partialは,class,struct,
又はintarfaceの直前に現れなけばならない。
部分型宣言の個々には,partial修飾子が含まれていなければならず,別々の部分と同じ名前空間内又
は包含型の内部で宣言されていなければならない。partial修飾子は,型宣言が別の場所にも存在するか
もしれないことを指示するが,その別の場所での存在は必すではない。partial修飾子を含んだ型宣言が
一つだけであっても正当である。
部分型のすべての部分は,コンパイル時にマージできるようにまとめてコンパイルされなければならな
い。部分型は,既にコンパイルされた型を拡張させることはできない。入れ子クラスは,partial修飾子
を使って複数の部分で宣言できる。通常,包含型もpartialを使って宣言され,入れ子型の個々の部分
が,包含型の別々の部分の内部で宣言される。
例 次の部分型は二つの部分で実装されている。これらの二つの部分は,別々のソースファイルに存
在する。最初の部分は,データベース写像ツールによって機械生成され,2番目の部分は人手で
書かれたものである。
public partial class Customer
{
private int id;
private string name;
private string address;
private List<Order> orders;
public Custome() {
...
291
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
public partial class Customer
{
public void SubmitOrder(Order orderSubmitted) {
orders.Add(orderSubmitted);
}
public bool HasOutStandingOrders() {
return orders.Count > 0;
}
}
上の二つの部分がまとめてコンパイルされると,コンパイル結果のコードは,このクラスが次
のような一つのコンパイル単位で記述されたかのように振る舞う。
public class Customer
{
private int id;
private string name;
private string address;
private List<Order> orders;
public Custome() {
...
}
public void SubmitOrder(Order orderSubmitted) {
orders.Add(orderSubmitted);
}
public bool HasOutStandingOrders() {
return orders.Count > 0;
}
}
部分宣言の別々の部分における型又は型仮引数に対して指定する属性の扱いは24.2で説明する。
17.2 クラスメンバ
クラスのメンバは,≪クラスメンバ宣言≫によって導入されるメンバ及び直接基底クラスから継承した
メンバで構成される。
≪クラスメンバ宣言群≫:
≪クラスメンバ宣言≫
≪クラスメンバ宣言群≫ ≪クラスメンバ宣言≫
292
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪クラスメンバ宣言≫:
≪定数宣言≫
≪フィールド宣言≫
≪メソッド宣言≫
≪特性宣言≫
≪イベント宣言≫
≪添字子宣言≫
≪演算子宣言≫
≪構築子宣言≫
≪終了化子宣言≫
≪静的構築子宣言≫
≪型宣言≫
クラスのメンバは,次の部類に分けられる。
− 定数。これはそのクラスに関連する定数を表現する(17.3参照)。
− フィールド。これはそのクラスの変数とする(17.4参照)。
− メソッド。これは非総称及び総称の両方であって,そのクラスで実行される計算及び動作を実装する
(17.5,25.6参照)。
− 特性。これは名前付けた特質及びそれら特質の読み書きに関連した動作を定義する(17.6参照)。
− イベント。これはクラスで生成される通知を定義する(17.7参照)。
− 添字子。これは配列と同じ方法でクラスのインスタンスに添字指定できる(17.8参照)。
− 演算子。クラスのインスタンスに適用できる式演算子を定義する(17.9参照)。
− インスタンス構築子。これはクラスのインスタンスを初期化するのに要求される動作を実装する
(17.10参照)。
− 終了化子。これはクラスのインスタンスが完全に廃棄される前に実行されるべき動作を実装する
(17.12参照)。
− 静的構築子。これはクラス自身を初期化するために要求される動作を実装する(17.11参照)。
− 型。これはクラスに対して局所的である型を表す(16.6参照)。
実行可能コードを格納できるメンバは,まとめてそのクラスの関数メンバともいう。クラスの関数メン
バは,そのクラスのメソッド,特性,イベント,添字子,演算子,インスタンス構築子,終了化子及び静
的構築子とする。
≪クラス宣言≫は新しい宣言空間(10.3参照)を生成し,≪型仮引数≫及び≪クラスメンバ宣言群≫は,
この宣言空間に新しいメンバを導入する≪クラス宣言≫に直接格納される。次の規則が≪クラスメンバ宣
言群≫に適用される。
− インスタンス構築子,終了化子及び静的構築子は,すぐ外側の囲んでいるクラスと同じ名前でなけれ
ばならない。その他すべてのメンバは,すぐ外側の囲んでいるクラスの名前とは異なる名前でなけれ
ばならない。
− クラス宣言の≪型仮引数並び≫内の型仮引数の名前は,同一の≪型仮引数並び≫内の別の型仮引数の
名前と異なっていなければならず,クラス名及びそのクラスのすべてのメンバの名前と異なっていな
ければならない。
− 型の名前は,同一クラス内で宣言された型でないメンバすべての名前と異なっていなければならない。
293
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
二つ以上の型宣言が同一完全限定名を共有する場合,その宣言は部分修飾子(17.1.4参照)をもたな
ければならず,これらの宣言を一つの型を定義するために結合させる。
注記 型宣言の完全限定名は型仮引数の数を符号化するので,型仮引数の数が異なる場合に限り,異
なる二つの型が同じ名前を共有することができる。
− 定数,フィールド,特性,又はイベント型の各名前は,同一クラス内で宣言されている他のすべての
メンバと異なる名前でなければならない。
− メソッドの名前は,同一クラスで宣言されている他のすべてのメソッド以外の名前とは異なっていな
ければならない。さらに,メソッドの呼出し情報(10.6参照)は,同一クラス内で宣言された他のす
べてのメソッドの呼出し情報と異なっていなければならない。また,同一クラス内で宣言された二つ
のメソッドは,refとoutの違いしかない呼出し情報をもつことはできない。
− インスタンス構築子の呼出し情報は,同一クラス内で宣言された他のすべてのインスタンス構築子の
呼出し情報と異なっていなければならず,同一クラス内で宣言された二つのインスタンス構築子は,
refとoutとによる違いしかない呼出し情報をもつことはできない。
− 添字子の呼出し情報は,同一クラス内で宣言されている他のすべての添字子の呼出し情報と異なって
いなければならない。
− 演算子の呼出し情報は,同一クラス内で宣言されている他のすべての演算子と異なっていなければな
らない。
クラスが継承したメンバ(17.2.1参照)は,そのクラスの宣言空間の一部とはならない。
注記 すなわち,派生クラスは継承したメンバと同じ名前,同じ呼出し情報でメンバを宣言すること
ができる。これは,実質上継承したメンバを隠ぺいする効果をもつ。
複数の部分で宣言された型(17.1.4参照)のメンバの集合は,個々の部分で宣言されたメンバの和集合
である。型宣言のすべての部分の本体は,同一の宣言空間(10.3参照)を共有し,個々のメンバの有効範
囲(10.7参照)はすべての部分の本体にまで拡張される。任意のメンバのアクセス可能領域は,常に囲ん
でいる型のすべての部分を含む。ある部分で宣言されたprivateメンバは,他の部分から自由にアクセ
スできる。同じメンバが複数の部分で宣言された場合,そのメンバがpartial修飾子をもった型でない
限り,コンパイル時エラーとする。
例 次に例を示す。
partial class A
{
int x; // Error, cannot declare x more than once
partial class Inner // OK, Inner is a partial type
{
int y;
}
}
partial class A
{
int x; // Error, cannot declare x more than once
294
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
partial class Inner // OK, Inner is a partial type
{
int z;
}
}
複数の部分で宣言された型の内部のメンバの順序は未定義とする。
注記 型の内部のメンバの順序は C# のコードでは重要ではないが,他の言語及び環境とインタフェ
ースを取るときは,重要になってくるかもしれない。
17.2.1 継承
クラスは,直接基底クラスのメンバを継承する。継承とは,基底クラスのインスタンス構築子,終了化
子及び静的構築子を除いて,クラスが,暗黙に直接基底クラスのすべてのメンバを格納することを意味す
る。継承の重要な点は次のとおりである。
− 継承は推移的である。CがBから派生し,BがAから派生している場合,CはBで宣言されたメンバ
と同時にAで宣言されたメンバを継承する。
− 派生クラスは,直接基底クラスを拡張する。派生クラスは,継承するメンバに新規メンバを追加でき
るが,継承したメンバの定義は取り除けない。
− インスタンス構築子,終了化子及び静的構築子は継承されないが,他のすべてのメンバは,宣言され
たアクセス可能性(10.5参照)に関係なく継承される。ただし,宣言されたアクセス可能性によって
は,派生クラスでは,継承したメンバにアクセスできないことがある。
− 派生クラスは,同じ名前又は同じ呼出し情報をもったメンバをnewで修飾して宣言することによって,
継承したメンバを隠ぺい(10.7.1.2参照)することができる。しかし,継承したメンバを隠ぺいしても,
そのメンバが削除されるわけではない。単に,派生クラス内でのメンバ検索によって見つけることが
できないだけである。
− クラスのインスタンスには,そのクラス及びその基底クラス内で宣言されたすべてのインスタンスフ
ィールドの集合を格納する。また,派生クラス型から基底クラス型への暗黙の変換が存在する(13.1.4
参照)。したがって,派生クラスのインスタンスへの参照は,基底クラスのインスタンスへの参照とし
て処理できる。
− クラスは,仮想メソッド,特性,添字子,及びイベントを宣言できる。派生クラスはこれらの関数の
メンバの実装を上書きできる。これによって,クラスは多相的な振る舞いができる。この動作の中で,
関数のメンバ呼出しによって実行される動作は,その関数のメンバが呼び出されるインスタンスの実
行時の型によって異なる。
− 構築総称型から継承したメンバは,型置換の後で継承される。つまり,メンバの成分要素型は,基底
クラス宣言の型仮引数をそのクラスの≪クラス基底≫指定内の対応する型実引数で置換した型をもつ。
例 次に例を示す。
class A<S>
{
public S field;
}
class B<T> : A<T[]>
295
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
// inherited: public T[] field;
public A<T> Meth(T t) { ... }
}
class C : B<string>
{
// inherited: public string[] field ;
// inherited: public A<string> Meth(string t);
}
クラスB<T>は,型仮引数Sが型実引数T[]で置換された後で,A<S>からメンバfieldを継
承する。同様に,クラスCは,型仮引数Tを型実引数stringで置換した後でB<T>のメンバ(継
承したメンバfieldを含む)を継承する。
17.2.2 new修飾子
≪クラスメンバ宣言≫では,継承したメンバと同じ名前又は同じ呼出し情報をもつメンバを宣言できる。
これを,派生クラスのメンバが基底クラスのメンバを隠ぺいするという。あるメンバが,継承したメンバ
を隠ぺいしたときの正確な規定は10.7.1.2で行う。
継承したメンバMがアクセス可能であり,Mを隠ぺいしている別の継承したアクセス可能なメンバNが
なければ,継承したメンバMは利用可能とみなされる。
利用可能な継承したメンバの隠ぺいは,エラーとはみなされないが,コンパイルは警告を出す。警告を
無効にするには,派生クラスのメンバ宣言にnew修飾子を含めることができる。これで,派生メンバが基
底メンバを隠ぺいすることを明示できる。入れ子型の一つ以上の部分宣言(17.1.4参照)がnew修飾子を
含み,その入れ子型が利用可能な継承したメンバを隠ぺいする場合,警告は出さない。
new修飾子が利用可能な継承したメンバを隠ぺいしない宣言内に含まれている場合,その旨(newによ
る隠ぺいがなされない)の警告を出す。
17.2.3 アクセス修飾子
≪クラスメンバ宣言≫はpublic,protected internal,protected,internal又はprivate
の5種類の宣言されたアクセス可能性(10.5.1参照)のいずれかをもつことができる。protected
internalを除いて,二つ以上のアクセス修飾子を指定するとコンパイル時エラーになる。≪クラスメン
バ宣言≫にアクセス修飾子が含まれない場合は,privateが仮定される。
17.2.4 成分要素型
メンバの宣言で使用される型は,そのメンバの成分要素型と呼ばれる。成分要素型には,定数,フィー
ルド,特性,イベント若しくは添字子の型,メソッド若しくは演算子の返却値の型,及びメソッド,添字
子,演算子若しくはインスタンス構築子の仮引数の型がある。メンバの成分要素型は,そのメンバ自身と
少なくとも同じアクセス可能性でなければならない(10.5.4参照)。
17.2.5 静的メンバとインスタンスメンバ
クラスのメンバは,静的メンバ又はインスタンスメンバのいずれかとする。
注記 一般的にいえば,静的メンバはクラスに所属し,インスタンスメンバはオブジェクト(クラス
のインスタンス)に所属するものと考えるとよい。
フィールド,メソッド,特性,イベント,演算子又は構築子の宣言にstatic修飾子が含まれる場合は,
296
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
静的メンバを宣言する。さらに,定数宣言又は型宣言では,暗黙に静的メンバを宣言する。静的メンバは
次の特徴をもつ。
− 静的メンバがE.M形式の≪メンバアクセス≫(14.5.4参照)で参照されている場合,EはメンバMを
もつ型を示さなければならない。Eがインスタンスを示している場合,コンパイル時エラーとする。
− 非総称クラスの静的フィールドは正確に一つの記憶域を表す。非総称クラスのインスタンスが幾つ生
成されても,静的フィールドのコピーは一つだけとする。個々の閉構築型(25.5.2参照)は,そのイ
ンスタンスの数に関係なく,静的フィールドの自分自身の集合をもつ。
− 静的関数のメンバ(メソッド,特性,イベント,演算子又は構築子)は,特定のインスタンスには作
用しないので,静的関数のメンバでthisを参照するとコンパイル時エラーになる。
フィールド,メソッド,特性,イベント,添字子,構築子又は終了化子の宣言にstatic修飾子が含ま
れない場合は,インスタンスメンバの宣言となる。インスタンスメンバは非静的メンバと呼ばれることも
ある。インスタンスメンバには次の特徴がある。
− インスタンスメンバが形式E.Mの≪メンバアクセス≫(14.5.4参照)で参照される場合,Eはメンバ
Mをもつ型のインスタンスを示さなければならない。Eが型を示す場合,コンパイル時エラーとする。
− クラスのどのインスタンスにも,そのクラスのすべてのインスタンスフィールドの個々の集合が格納
される。
− インスタンス関数メンバ(メソッド,特性,添字子,イベント,インスタンス構築子又は終了化子)
は,そのクラスの指定されたインスタンスを処理し,このインスタンスはthis(14.5.7参照)として
アクセスできる。
例 次の例は,静的メンバ及びインスタンスメンバへのアクセス規則を例示する。
class Test
{
int x;
static int y;
void F() {
x = 1;
// Ok, same as this.x = 1
y = 1;
// Ok, same as Test.y = 1
}
static void G() {
x = 1;
// Error, cannot access this.x
y = 1;
// Ok, same as Test.y = 1
}
static void Main() {
Test t = new Test();
t.x = 1;
// Ok
t.y = 1;
// Error, cannot access static member through
instance
Test.x = 1;
// Error, cannot access instance member through
type
Test.y = 1;
// Ok
297
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
メソッドFは,インスタンス関数メンバにおいて,≪単純名≫(14.5.2参照)がインスタンス
メンバ及び静的メンバの両方にアクセスするために使用できることを示す。メソッドGは,静的
関数のメンバにおいて,≪単純名≫を使ってインスタンスメンバにアクセスするとコンパイル時
エラーになることを示す。Mainメソッドは,≪メンバアクセス≫(14.5.4参照)において,イン
スタンスメンバはインスタンスを介してアクセスしなければならず,静的メンバは型を介してア
クセスしなければならないことを示す。
17.2.6 入れ子型
クラス又は構造体内で宣言された型を,入れ子型という。コンパイル単位又は名前空間内で宣言された
型は,入れ子でない型という。
例 次に例を示す。
using System;
class A
{
class B
{
static void F() {
Console.WriteLine("A.B.F");
}
}
}
この例では,クラスBは,クラスA内で宣言されているので,入れ子型である。クラスAはコ
ンパイル単位内で宣言されているので,入れ子でない型となる。
17.2.6.1 完全限定名
入れ子型宣言に対する完全限定名(10.8.2参照)はS.Nとする。ここで,Sは,型Nが宣言されている
型宣言の完全限定名とし,Nは入れ子型宣言の非限定名であるとする(任意の≪総称次元指定子≫を含む)。
17.2.6.2 宣言されたアクセス可能性
入れ子でない型は,公開及び内部と宣言されたアクセス可能性をもつことができ,省略時には内部と宣
言されたアクセス可能性をもつ。入れ子型のアクセス可能性の宣言は,これらの形式を使えるだけでなく,
追加形式も使うことができる。どのような形式を使うかは,宣言を包含する型がクラスと構造体のいずれ
であるかによって決まる。
− クラス内で宣言された入れ子型は,五つのアクセス可能性(公開,内部限定公開,限定公開,内部又
は非公開)の宣言形式のいずれかをもつことができ,他のクラスメンバと同じように,省略時には非
公開と宣言されたアクセス可能性をもつ。
− 構造体内で宣言された入れ子型は,三つのアクセス可能性(公開,内部又は非公開)の宣言形式のい
ずれかをもつことができ,他の構造体メンバと同じように,省略時には非公開宣言されたアクセス可
能性をもつ。
例 次に例を示す。
public class List
298
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
// Private data structure
private class Node
{
public object Data;
public Node Next;
public Node(object data, Node next) {
this.Data = data;
this.Next = next;
}
}
private Node first = null;
private Node last = null;
// Public interface
public void AddToFront(object o) {…}
public void AddToBack(object o) {…}
public object RemoveFromFront() {…}
public object AddToFront() {…}
public int Count { get {…} }
}
この例は非公開の入れ子になったクラスNodeを宣言する。
17.2.6.3 隠ぺい
入れ子型クラスは基底メンバを隠ぺい(10.7.1.1参照)できる。入れ子型の宣言にはnew修飾子を用い
て,隠ぺいを明示的に表現できる。
例 次に例を示す。
using System;
class Base
{
public static void M() {
Console.WriteLine("Base.M");
}
}
class Derived: Base
{
new public class M
{
public static void F() {
Console.WriteLine("Derived.M.F");
}
}
299
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
class Test
{
static void Main() {
Derived.M.F();
}
}
この例は,Baseで定義されたメソッドMを隠ぺいする入れ子になったクラスMを示す。
17.2.6.4 thisアクセス
入れ子型及びそれを包含する型は,≪thisアクセス≫(14.5.7参照)に関して特別な関係をもたない。
具体的には,入れ子型の中でthisを使って,それを包含する型のインスタンスメンバを参照することが
できない。入れ子型が,それを包含する型のインスタンスメンバにアクセスする必要がある場合は,入れ
子型の構築子の実引数として,包含する型のインスタンスのthisを渡すことでアクセスが可能になる。
例 次に例を示す。
using System;
class C
{
int i = 123;
public void F() {
Nested n = new Nested(this);
n.G();
}
public class Nested
{
C this̲c;
public Nested(C c) {
this̲c = c;
}
public void G() {
Console.WriteLine(this̲c.i);
}
}
}
class Test
{
static void Main() {
C c = new C();
c.F();
}
}
300
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
このコード例は,その手法を示す。CのインスタンスはNestedのインスタンスを作成し,そ
して,Cのインスタンスメンバへのアクセスを後で提供するために,それ自身であるthisを
Nestedの構築子に渡す。
17.2.6.5 包含する型の非公開メンバ及び限定公開メンバへのアクセス
入れ子型は,非公開宣言及び限定公開と宣言されたアクセス可能性をもつ包含する型のメンバを含めて,
その包含する型にアクセス可能なメンバすべてにアクセスできる。
例 次に例を示す。
using System;
class C
{
private static void F() {
Console.WriteLine("C.F");
}
public class Nested
{
public static void G() {
F();
}
}
}
class Test
{
static void Main() {
C.Nested.G();
}
}
この例では,入れ子クラスNestedを包含するクラスCを示す。Nestedクラス内で,メソッ
ドGはクラスCで定義された静的メソッドFを呼び出す。このメソッドFは非公開の宣言された
アクセス可能性をもつ。
また,入れ子型は,それを包含する型の基底型で定義された限定公開メンバにもアクセスできる。
例 次に例を示す。
using System;
class Base
{
protected void F() {
Console.WriteLine("Base.F");
}
}
class Derived: Base
{
301
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public class Nested
{
public void G() {
Derived d = new Derived();
d.F();
// ok
}
}
}
class Test
{
static void Main() {
Derived.Nested n = new Derived.Nested();
n.G();
}
}
入れ子クラスDerived.NestedはDerivedのインスタンスを通した呼出しによって,
Derivedの基底クラスBaseで定義された限定公開メソッドFにアクセスする。
17.2.7 予約されたメンバ名
基礎となるC#実行時実装のために,特性,イベント又は添字子のソースメンバ宣言に対して,メンバ宣
言の種類,その名前及びその型を基にした二つのメソッド呼出し情報を,実装では予約しなければならな
い(17.2.7.1,17.2.7.2及び17.2.7.3参照)。プログラムで,これらの予約された呼出し情報の一つに一致す
る呼出し情報をもつメンバを宣言すると,たとえ基礎となる実行時実装が予約された呼出し情報を使用し
ない場合であってもコンパイル時エラーとする。
予約された名前が宣言を導入することはない。このため,予約された名前はメンバ検索の対象にならな
い。しかし,予約されたメソッド呼出し情報に関連した宣言は,継承(17.2.1参照)の対象になり,new
修飾子(17.2.2参照)で隠ぺいされることもあり得る。
注記 これらの名前の予約には,次の三つの目的がある。
1) 基礎となる実装機能が,C#言語機能へのgetアクセス又はsetアクセスに対して通常の
識別子をメソッド名として使用できるようにする。
2) 他の言語が,C#言語機能へのgetアクセス又はsetアクセスに対して通常の識別子をメ
ソッド名として相互運用できるようにする。
3) すべてのC#実装で,予約メンバ名について一貫性を保つことで,ある規格適合コンパイラ
によって受け入れられたソースが別の規格適合コンパイラでも受け入れられるようにする。
終了化子(17.12参照)の宣言も,呼出し情報を予約する(17.2.7.4参照)。
17.2.7.1 特性用に予約されたメンバ名
型Tの特性P(17.6参照)に対して,次の呼出し情報が予約される。
T get̲P();
void set̲P(T value);
特性が読込み専用又は書出し専用であっても,両方の呼出し情報が予約される。
例 次に例を示す。
302
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class A
{
public int P {
get { return 123; }
}
}
class B: A
{
new public int get̲P() {
return 456;
}
new public void set̲P(int value) {
}
}
class Test
{
static void Main() {
B b = new B();
A a = b;
Console.WriteLine(a.P);
Console.WriteLine(b.P);
Console.WriteLine(b.get̲P());
}
}
クラスAは,読込み専用特性Pを定義している。すなわち,予約される呼出し情報はget̲P
メソッドとset̲Pメソッドである。クラスAから派生したクラスBは,これらの予約された呼
出し情報の両方を隠ぺいする。この例では,次のように出力される。
123
123
456
17.2.7.2 イベント用に予約されたメンバ名
委譲型TのイベントE(17.7参照)に対して,次の呼出し情報が予約される。
void add̲E(T handler);
void remove̲E(T handler);
17.2.7.3 添字子用に予約されたメンバ名
仮引数並びLをもつ型Tの添字子(17.8参照)に対して,次の呼出し情報が予約される。
T get̲Item(L);
void set̲Item(L, T value);
添字子が読込み専用又は書出し専用であっても,両方の呼出し情報が予約される。
303
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
17.2.7.4 終了化子用に予約されたメンバ名
終了化子(17.12参照)を含んでいるクラスに対して,次の呼出し情報が予約される。
void Finalize();
17.3 定数
定数は,コンパイル時に算出できる定数値を表現するクラスメンバとする。≪定数宣言≫は,指定され
た型の一つ以上の定数を導入する。
≪定数宣言≫:
≪属性群≫opt ≪定数修飾子群≫opt const ≪型≫ ≪定数宣言子群≫ ;
≪定数修飾子群≫:
≪定数修飾子≫
≪定数修飾子群≫ ≪定数修飾子≫
≪定数修飾子≫:
new
public
protected
internal
private
≪定数宣言子群≫:
≪定数宣言子≫
≪定数宣言子群≫ , ≪定数宣言子≫
≪定数宣言子≫:
≪識別子≫ = ≪定数式≫
≪定数宣言≫は,≪属性≫(箇条24参照)の集合,new修飾子(17.2.2参照)及び四つのアクセス修飾
子(17.2.3参照)の正しい組合せを含むことができる。属性及び修飾子は,≪定数宣言≫で宣言されたす
べてのメンバに適用される。定数が静的メンバとみなされる場合でも,≪定数宣言≫にstatic修飾子は
必要なく,また,使用することもできない。一つの定数宣言内で同じ修飾子を複数回使用すると,(コンパ
イル時)エラーになる。
≪定数宣言≫の≪型≫は,宣言で導入されるメンバの型を指定する。この型の後には,≪定数宣言子≫
の並びが続く。各宣言子によって新規のメンバが導入される。≪定数宣言子≫は,メンバを名前付ける≪
識別子≫,字句“=”,メンバの値を与える≪定数式≫(14.16参照)の順で構成される。
定数宣言で指定される≪型≫は,sbyte,byte,short,ushort,int,uint,long,ulong,char,
float,double,decimal,bool,string,≪列挙型≫又は≪参照型≫でなければならない。個々の
≪定数式≫は,目的の型の値又は,暗黙の変換(13.1参照)によって目的の型に変換できる型の値になら
なければならない。
定数の≪型≫は,少なくともその定数自身と同じアクセス可能性でなければならない(10.5.4参照)。
定数の値は,≪単純名≫(14.5.2参照)又は≪メンバアクセス≫(14.5.4参照)を使った式で得られる。
定数は,それ自身を≪定数式≫に含めることができる。このため,定数は,≪定数式≫を必要とする構
築要素に使用できる。
注記 この構成要素は,caseラベル,goto case文,enumメンバ宣言,属性及び他の定数宣言を
含む。
304
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
14.16で説明するように,≪定数式≫は,コンパイル時に完全に評価され得る式である。
string以外の≪参照型≫で非null値を作成する唯一の方法はnew演算子を適用することで
あり,new演算子は≪定数式≫内には記述できないため,≪参照型≫の定数値がとりうる値は
string以外ではnullだけである。
定数値に記号名を必要とする場合で,その値の型が定数宣言内で許されていない場合,又は,その値が
≪定数式≫でコンパイル時に計算できない場合,readonlyフィールド(17.4.2参照)が代わりに使える。
注記 constとreadonlyとの版管理上の意味は異なる(17.4.2.2参照)。
複数の定数を宣言する定数宣言は,同じ属性,同じ修飾子及び同じ型の単一の定数を複数回宣言するの
と同等とする。
例 次に例を示す。
class A
{
public const double X = 1.0, Y = 2.0, Z = 3.0;
}
これは,次と同じ意味になる。
class A
{
public const
double X = 1.0;
public const
double Y = 2.0;
public const
double Z = 3.0;
}
定数は,依存関係が循環しない限り,同一プログラム内の他の定数に依存していてもよい。コンパイラ
は定数宣言を適切な順序に自動的に並べ替えて評価する。
例 次に例を示す。
class A
{
public const int X = B.Z + 1;
public const int Y = 10;
}
class B
{
public const int Z = A.Y + 1;
}
これは,コンパイラが最初にA.Yを評価し,次にB.Zを評価し,最後にA.Xを評価する。こ
れによって,値10,11及び12を生成する。
定数宣言は他のプログラムの定数に依存できる。しかし,この依存性は一方向からだけ可能である。
例 上の例では,もしもAとBとが異なるプログラムで宣言されているならば,A.XがB.Zに依存
305
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
できる場合は,B.Zが同時にA.Yに依存することはできない。
17.4 フィールド
フィールドは,オブジェクト又はクラスに関連付けられた変数を表現するメンバとする。≪フィールド
宣言≫は,指定された型の一つ以上のフィールドを導入する。
≪フィールド宣言≫:
≪属性群≫opt ≪フィールド修飾子群≫opt ≪型≫ ≪変数宣言子群≫ ;
≪フィールド修飾子群≫:
≪フィールド修飾子≫
≪フィールド修飾子群≫ ≪フィールド修飾子≫
≪フィールド修飾子≫:
new
public
protected
internal
private
static
readonly
volatile
≪変数宣言子群≫:
≪変数宣言子≫
≪変数宣言子群≫ , ≪変数宣言子≫
≪変数宣言子≫:
≪識別子≫
≪識別子≫ = ≪変数初期化子≫
≪変数初期化子≫:
≪式≫
≪配列初期化子≫
≪フィールド宣言≫は≪属性≫(箇条24参照)の集合,new修飾子(17.2.2参照),四つのアクセス修
飾子(17.2.3参照)の正しい組合せ及びstatic修飾子(17.4.1参照)を含むことができる。さらに,≪
フィールド宣言≫は,readonly修飾子(17.4.2参照)又はvolatile修飾子(17.4.3参照)を含むこと
ができるが,両方を含めてはならない。属性及び修飾子は,≪フィールド宣言≫によって宣言されたメン
バのすべてに適用される。一つのフィールド宣言内で同じ修飾子を複数回使用すると,(コンパイル時)エ
ラーになる。
≪フィールド宣言≫の≪型≫は,その宣言で導入されるメンバの型を指定する。この型の後には,≪変
数宣言子≫の並びが続く。各宣言子によって新規のメンバが導入される。≪変数宣言子≫は,メンバを名
前付ける≪識別子≫,それに続く省略可能な“=”字句及びそのメンバの初期値を与える≪変数初期化子
≫(17.4.5参照)で構成される。
フィールドの≪型≫は少なくともそのフィールド自身にアクセス可能でなければならない(10.5.4参照)。
フィールドの値は≪単純名≫(14.5.2参照),≪メンバアクセス≫(14.5.4参照),≪thisアクセス≫(14.5.7
参照)又は≪baseアクセス≫(14.5.8参照)を使った式の中で取得できる。非読込み専用フィールドの値
306
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
は≪代入≫(14.14参照)を使って更新できる。非読込み専用のフィールドの値は後置増加演算子及び後置
減少演算子(14.5.9参照)と,前置増加演算子及び前置減少演算子(14.6.5参照)を使って取得,更新で
きる。
複数のフィールドを宣言するフィールド宣言は,同じ属性,同じ修飾子及び同じ型の単一の定数を複数
回宣言するのと同等である。
例 次に例を示す。
class A
{
public static int X = 1, Y, Z = 100;
}
これは,次と同じ意味になる。
class A
{
public static int X = 1;
public static int Y;
public static int Z = 100;
}
17.4.1 静的フィールド及びインスタンスフィールド
フィールド宣言にstatic修飾子を含んでいる場合,その宣言によって導入されたフィールドは静的フ
ィールドという。static修飾子が現れなければ,その宣言によって導入されたフィールドはインスタン
スフィールドという。静的フィールド及びインスタンスフィールドはC#で使える数種類の変数(箇条12
参照)の二つである。それらを,場合によっては,それぞれ静的変数及びインスタンス変数ともいう。
17.2.5で説明するように,クラスの個々のインスタンスは,そのクラスのインスタンスフィールドの完
全な集合を格納する。一方,個々の非総称クラス又は閉構築型の静的フィールドは,そのインスタンス数
に関係なく,ただ一つだけである。
17.4.2 読込み専用フィールド
≪フィールド宣言≫にreadonly修飾子を含む場合,その宣言によって導入されたフィールドは読込み
専用フィールドという。読込み専用フィールドへの直接代入は,その宣言の一部としてだけ,又は同一ク
ラスのインスタンス構築子若しくは静的構築子の中でだけ行うことができる。読込み専用フィールドは,
これらの文脈では複数回代入できる。具体的には,readonlyフィールドへの直接代入は,次の文脈にお
いてだけ許される。
− フィールドを導入する≪変数宣言子≫内。宣言に≪変数初期化子≫を含める。
− インスタンスフィールドの場合は,フィールド宣言を格納するクラスのインスタンス構築子内。静的
フィールドの場合は,そのフィールド宣言を格納するクラスの静的構築子内。out仮引数又はref仮
引数としてreadonlyフィールドを渡すのもこれらの文脈でだけ許される。
上記以外の文脈で,readonlyフィールドに代入しようとしたり,readonlyフィールドをout仮引数
又はref仮引数として渡そうとしたりすると,コンパイル時エラーになる。
17.4.2.1 定数に静的読込み専用フィールドを利用する
static readonlyフィールドは,定数値として記号名を使いたいが,その定数値の型がconst宣言内
で許されていない場合,又は,コンパイル時に定数値が計算できない場合に便利である。
307
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例 次に例を示す。
public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);
private byte red, green, blue;
public Color(byte r, byte g, byte b) {
red = r;
green = g;
blue = b;
}
}
この例では,Black,White,Red,Green及びBlueのメンバはconstメンバとして宣言で
きない。なぜなら,それらの値はコンパイル時に計算できないからである。しかし,その代わり
にそれらをstatic readonly宣言することで,同じ効果が得られる。
17.4.2.2 定数及び静的読込みフィールドの版管理
定数と読込み専用フィールドと異なるバイナリ版管理意味をもつ。式が定数を参照する場合,定数の値
はコンパイル時に取得されるが,式が読込み専用フィールドを参照する場合,そのフィールドの値は実行
時まで取得されない。
例 二つの別々のプログラムで構成されるアプリケーションを考える。
namespace Program1
{
public class Utils
{
public static readonly int X = 1;
}
}
using System;
namespace Program2
{
class Test
{
static void Main() {
Console.WriteLine(Program1.Utils.X);
}
}
}
308
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
名前空間Program1及び名前空間Program2は,それぞれ別々にコンパイルされる二つのプ
ログラムを示す。Program1.Utils.Xは静的読込みフィールドとして宣言されているので,
Console.WriteLine文によって出力される値はコンパイル時には不明となり,代わりに実行時
に取得される。すなわち,Xの値が変更され,Program1が再コンパイルされると,
Console.WriteLine文はProgram2が再コンパイルされなくても,新しい値を出力する。し
かし,Xが定数の場合,Xの値はProgram2がコンパイルされたときに取得され,Program2が
再コンパイルされるまで,Program1の変更による影響を受けない。
17.4.3 揮発性フィールド
≪フィールド宣言≫がvolatile修飾子を含んでいる場合,その宣言によって導入されるフィールドは
揮発性フィールドとする。非揮発性フィールドの場合は,命令を並べ替える最適化手法が用いられると,
≪lock文≫(15.12参照)によって提供されるような同期化を行わずにフィールドにアクセスするマルチ
スレッドプログラムにおいて,予測不能な予期しない結果が生じることがある。これらの最適化を実行す
るのは,コンパイラ,実行時システム又はハードウェアである。揮発性フィールドの場合は,並べ替えに
よる最適化が次のように制限される。
− 揮発性フィールドの読込みは,揮発性読込みという。揮発性読込みは“取得形式”であって,命令列
でその後に行われるどのメモリ参照よりも前に行われることが保証される。
− 揮発性フィールドへの書出しは,揮発性書出しと呼ばれる。揮発性書出しは“解放形式”であって,
命令列の書出し命令の前に行われるどのメモリ参照よりも後に行われることが保証される。
これらの制限によって,他のスレッドによる揮発性書出しは,それらが実行された順にすべてのスレッ
ドで行われることが保証される。規格適合処理系では,すべての実行スレッドから参照できる揮発性書出
しの一つの完全な順序付けを用意する必要はない。揮発性フィールドの型は次の一つでなければならない。
− ≪参照型≫。
− 参照型(25.7参照)であることが分かっている≪型仮引数≫。
− byte,sbyte,short,ushort,int,uint,char,float又はboolの型。
− 列挙基底型としてbyte,sbyte,short,ushort,int又はuintをもつ≪列挙型≫。
例 次に例を示す。
using System;
using System.Threading;
class Test
{
public static int result;
public static volatile bool finished;
static void Thread2() {
result = 143;
finished = true;
}
static void Main() {
finished = false;
// Run Thread2() in a new thread
new Thread(new ThreadStart(Thread2)).Start();
309
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
// Wait for Thread2 to signal that it has a result by setting
// finished to true.
for (;;) {
if (finished) {
Console.WriteLine("result = {0}", result);
return;
}
}
}
}
この例では,次のように出力される。
result = 143
この例では,メソッドMainはメソッドThread2を実行する新しいスレッドを開始する。こ
のメソッドはresultと呼ばれる非揮発性フィールドに値を格納する。そして,揮発性フィール
ドfinishedにtrueを格納する。主スレッドはフィールドfinishedにtrueが設定されるま
で待ち,そしてフィールドresultを読み込む。finishedがvolatile宣言されているので,
主スレッドはフィールドresultから値143を読み込まなければならない。もし,フィールド
finishedがvolatile宣言されていなければ,finishedへの格納の後,resultへの格納は
メインスレッドに対して可視になることも許されている。それゆえ,この場合,主スレッドはフ
ィールドresultから値0を読み込む。finishedをvolatileフィールドと宣言することで,
このような矛盾を起こさないようにできる。
17.4.4 フィールド初期化
静的フィールドであろうとインスタンスフィールドであろうと,フィールドの初期値はそのフィールド
の型の省略時の値とする。この省略時の初期化を行う前にフィールドの値を参照することはできない。こ
のため,フィールドが“未初期化”状態になることはない。
例 次に例を示す。
using System;
class Test
{
static bool b;
int i;
static void Main() {
Test t = new Test();
Console.WriteLine("b = {0}, i = {1}", b, t.i);
}
}
この例では,次のように出力される。
b = False, i = 0
なぜなら,bとiとの両方が自動的に省略時の値で初期化されるからである。
310
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
17.4.5 変数初期化子
フィールド宣言には≪変数初期化子≫を含めることができる。静的フィールドの場合,変数初期化子は,
クラスの初期化中に実行される代入文に相当する。インスタンスフィールドの場合,変数初期化子は,そ
のクラスのインスタンス生成時に実行される代入文に相当する。
例 次に例を示す。
using System;
class Test
{
static double x = Math.Sqrt(2.0);
int i = 100;
string s = "Hello";
static void Main() {
Test a = new Test();
Console.WriteLine("x = {0}, i = {1}, s = {2}", x, a.i, a.s);
}
}
この例では,次のように出力される。
x = 1.4142135623731, i = 100, s = Hello
なぜなら静的フィールド初期化子を実行する際にxへの代入が起こり,インスタンスフィール
ド初期化を実行する際にi及びsへの代入が起こるからである。
変数初期化子をもつフィールドを含むすべてのフィールドに対して,17.4.4で説明する省略時の値の初
期化が起こる。したがって,クラスが初期化されると,そのクラスのすべての静的フィールドは最初に省
略時の値に初期化され,その後,静的フィールド初期化子が記述順に実行される。同様に,クラスのイン
スタンスが生成されると,そのインスタンスのすべてのインスタンスフィールドは最初に省略時の値に初
期化され,その後,インスタンスフィールド初期化子が記述順に実行される。同一型に対する複数の部分
型宣言内でフィールド宣言がある場合,どの部分からどういう順番で行われるかは規定しないが,個々の
部分におけるフィールド初期化が記述順に実行される。
変数初期化子をもつ静的フィールドは,その省略時の値状態を参照できる。
例 しかし,これは書き方としては,避けるのがよい。次に,この振る舞いの例を示す。
using System;
class Test
{
static int a = b + 1;
static int b = a + 1;
static void Main() {
Console.WriteLine("a = {0}, b = {1}", a, b);
}
}
aとbが循環的に定義されているにもかかわらず,プログラムは有効である。出力は次のよう
になる。
311
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
a = 1, b = 2
なぜなら,静的フィールドa及びbは,それらの初期化子が実行される前に0(intに対する
省略時の値)に初期化されるからである。aに対する初期化子を実行する時点では,bの値は0
であり,したがってaは1に初期化される。bに対する初期化子を実行する時点では,aの値は
既に1であり,したがってbは2に初期化される。
17.4.5.1 静的フィールド初期化
クラス宣言の静的フィールドの変数初期化子は,そのクラス宣言に記述された順序どおりに実行される
一連の代入に相当する。クラス内に静的構築子(17.11参照)が存在するならば,その静的フィールド初期
化子が,その静的構築子の実行直前に起こる。それ以外の場合は,それぞれの実装に応じて,そのクラス
の静的フィールドが初めて使用される前に静的フィールド初期化子を実行する。
例 次に例を示す。
using System;
class Test
{
static void Main() {
Console.WriteLine("{0} {1}", B.Y, A.X);
}
public static int F(string s) {
Console.WriteLine(s);
return 1;
}
}
class A
{
public static int X = Test.F("Init A");
}
class B
{
public static int Y = Test.F("Init B");
}
これは,次のように出力されるかもしれない。
Init A
Init B
1 1
さもなければ,次のように出力されるかもしれない。いずれにしても,いずれかの出力になる。
Init B
Init A
1 1
なぜなら,Xの初期化子とYの初期化子の実行はいずれの順でも起こるからである。これらの
フィールドを参照する前には,初期化子が実行されているということだけが保証される。これと
312
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
は別の例を示す。
using System;
class Test
{
static void Main() {
Console.WriteLine("{0} {1}", B.Y, A.X);
}
public static int F(string s) {
Console.WriteLine(s);
return 1;
}
}
class A
{
static A() {}
public static int X = Test.F("Init A");
}
class B
{
static B() {}
public static int Y = Test.F("Init B");
}
出力は次のようにならなければならない。
Init B
Init A
1 1
なぜなら,静的構築子を実行するときの規則によって,Bの静的構築子(とBの静的フィール
ド初期化子)はAの静的構築子及びフィールド初期化子の前に実行されなければならないからで
ある。
17.4.5.2 インスタンスフィールド初期化
クラスのインスタンスフィールドの変数初期化子は,そのクラスのインスタンス構築子(17.10.2参照)
の実行開始直後に実行される一連の代入に対応する。変数初期化子は,クラス宣言に出てくる順番どおり
に実行される。クラスインスタンスの生成及び初期化処理は17.10にも示す。
インスタンスフィールドの変数初期化子は,生成中のインスタンスを参照できない。このため,変数初
期化子でthisを参照するとコンパイル時エラーになる。同様に,変数初期化子が≪単純名≫を使ってイ
ンスタンスメンバを参照するとコンパイル時エラーになる。
例 次に例を示す。
class A
{
int x = 1;
313
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int y = x + 1;
// Error, reference to instance member of this
}
yに対する変数初期化子が,生成しているインスタンスのメンバを参照しているので,コンパ
イル時エラーになる。
17.5 メソッド
メソッドは,オブジェクト又はクラスによって実行される計算又は動作を実装するメンバとする。メソ
ッドは≪メソッド宣言≫を使って宣言される。
≪メソッド宣言≫:
≪メソッドヘッダ≫ ≪メソッド本体≫
≪メソッドヘッダ≫:
≪属性群≫opt ≪メソッド修飾子≫opt ≪返却値の型≫ ≪メンバ名≫ ≪型仮引数
並び≫opt ( ≪仮引数並び≫opt ) ≪型仮引数制約群節群≫opt
≪メソッド修飾子群≫:
≪メソッド修飾子≫
≪メソッド修飾子群≫ ≪メソッド修飾子≫
≪メソッド修飾子≫:
new
public
protected
internal
private
static
virtual
sealed
override
abstract
extern
≪返却値の型≫:
≪型≫
void
≪メンバ名≫:
≪識別子≫
≪インタフェース型≫ . ≪識別子≫
≪メソッド本体≫:
≪ブロック≫
;
≪メンバ名≫は,メソッドの名前を指定する。メソッドが明示的なインタフェースメンバ実装(20.4.1
参照)でない限り,その≪メンバ名≫は単純に≪識別子≫とする。明示的なインタフェースメンバ実装の
場合,その≪メンバ名≫は≪インタフェース型≫,“.”及び≪識別子≫で構成される。
≪メソッド宣言≫は≪属性群≫(箇条24参照)の集合及び四つのアクセス修飾子(17.2.3参照)の正し
314
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
い組合せ,new(17.2.2参照),static(17.5.2参照),virtual(17.5.3参照),override(17.5.4参照),
sealed(17.5.5参照),abstract(17.5.6参照)及びextern(17.5.7参照)修飾子を含めることができ
る。
次の条件をすべて満たす宣言は,有効な修飾子の組合せとなる。
− アクセス修飾子(17.2.3参照)の正しい組合せを含む。
− 同じ修飾子が複数回含まれない。
− static,virtual及びoverrideを高々一つ含む。
− new及びoverrideを高々一つ含む。
− abstract修飾子を含む場合,static,virtual,sealed及びexternのいずれの修飾子も含ま
ない。
− private修飾子を含む場合,virtual,override及びabstractのいずれの修飾子も含まない。
− sealed修飾子を含む場合,override修飾子も同時に含む。
− 宣言が明示的なインタフェースメンバの実装(≪インタフェース型≫を含む≪メンバ名≫による指定,
20.4.1参照)の場合,extern修飾子が指定できる以外は,どの修飾子も含まれない。
メソッド宣言の≪返却値の型≫は,そのメソッドで計算して返される値の型を指定する。メソッドが値
を返さないならば,≪返却値の型≫はvoidとする。
省略可能な≪型仮引数並び≫は,総称メソッドの型仮引数を指定する。省略可能な≪型仮引数制約群節
群≫は,型仮引数に対する制約を指定する。≪型仮引数並び≫を含まない≪メソッド宣言≫は,≪型仮引
数制約群節群≫を含んではならない。明示的なインタフェースメンバ実装に対する≪メソッド宣言≫は,
≪型仮引数制約群節群≫を含んではならない。明示的なインタフェースメンバ実装に対する総称≪メソッ
ド宣言≫は,インタフェースメソッド上の制約を継承する。同様に,override修飾子を含むメソッド宣
言は,≪型仮引数制約群節群≫を含んではならず,そのメソッドの型仮引数に対する制約は,上書きされ
る仮想メソッドから継承されなければならない。総称メソッドは25.6で完全に規定される。
省略可能な≪仮引数並び≫はそのメソッドの仮引数(17.5.1参照)を指定する。
メソッドの≪返却値の型≫及び≪仮引数並び≫内で参照される型のそれぞれは,そのメソッド自身が少
なくともアクセスできなければならない(10.5.4参照)。
abstractメソッド及びexternメソッドにおいて,その≪メソッド本体≫は単にセミコロンで構成さ
れる。その他すべてのメソッドの場合,≪メソッド本体≫を構成するのは≪ブロック≫である。その≪ブ
ロック≫は,メソッド呼出し時に実行する文を指定する。
メソッドの名前,型仮引数の数及び仮引数並びは,そのメソッドの呼出し情報(10.6参照)を定義する。
具体的には,メソッドの呼出し情報は,その名前,型仮引数の数並びに仮引数の個数,≪仮引数修飾子≫
及び型で構成される。返却値の型,仮引数の名前,型仮引数の名前及び型仮引数の制約は,メソッドの呼
出し情報に含まれない。仮引数の型がメソッドの型仮引数を参照する場合,型仮引数の順序位置(型仮引
数の名前でなく)が型等価性に使われる。
メソッドの名前は,同一クラスで宣言されているメソッド以外の他のすべての名前と異なっていなけれ
ばならない。さらに,メソッドの呼出し情報は,同一クラス内で宣言されたすべての他のメソッドの呼出
し情報と異なっていなければならない。そして,同一クラス内で宣言された二つのメソッドはrefとout
だけしか違いのない呼出し情報をもつことはできない。
17.5.1 メソッド仮引数
メソッドの仮引数(ある場合)は,メソッドの≪仮引数並び≫によって宣言される。
315
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪仮引数並び≫:
≪固定仮引数群≫
≪固定仮引数群≫ , ≪仮引数配列≫
≪仮引数配列≫
≪固定仮引数群≫:
≪固定仮引数≫
≪固定仮引数群≫ , ≪固定仮引数≫
≪固定仮引数≫:
≪属性群≫opt ≪仮引数修飾子≫opt ≪型≫ ≪識別子≫
≪仮引数修飾子≫:
ref
out
≪仮引数配列≫:
≪属性群≫opt params ≪配列型≫ ≪識別子≫
仮引数並びは,一つ以上のコンマ区切り仮引数で構成され,その最後だけを≪仮引数配列≫にできる。
≪固定仮引数≫は省略可能な≪属性≫(箇条24参照)の集合,省略可能な一つのref修飾子又は一つ
のout修飾子,一つの≪型≫及び一つの≪識別子≫で構成される。それぞれの≪固定仮引数≫は,指定さ
れた名前で指定された型の仮引数を宣言する。
≪仮引数配列≫は省略可能な≪属性≫(箇条24参照)の集合,一つのparams修飾子,一つの≪配列
型≫,及び一つの≪識別子≫で構成される。仮引数配列は,指定された名前で指定された配列型の単一仮
引数を宣言する。仮引数配列の≪配列型≫は単一次元の配列型(19.1参照)としなければならない。メソ
ッド呼出しでは,仮引数配列は指定した配列型の実引数を一つ指定するか,配列要素型の実引数を0個以
上指定できる。仮引数配列については17.5.1.4にも示す。
メソッドの宣言は,型仮引数,仮引数,局所変数及び局所定数用の一つの個別の宣言空間を生成する。
メソッドの型仮引数並び,並びに仮引数並びによって,及びメソッドの≪ブロック≫内の局所変数宣言並
びに局所定数宣言によって,この宣言空間に名前が導入される。メソッド宣言空間の二つのメンバが同じ
名前をもつとコンパイル時エラーになる。メソッド宣言空間及び入れ子になった宣言空間の局所変数宣言
空間で同じ名前の要素が含まれるとエラーになる。
メソッド呼出し(14.5.5.1参照)は,その呼出しに固有なメソッドの仮引数及び局所変数のコピーを生成
する。そして,その生成された仮引数に,呼出しの実引数並びにある値又は変数参照が代入される。メソ
ッドの≪ブロック≫内では,仮引数は≪単純名≫式内のそれらの識別子によって参照できる(14.5.2参照)。
仮引数には次の四種類がある。
− 値仮引数。修飾子を使わずに宣言される。
− 参照仮引数。ref修飾子で宣言される。
− 出力仮引数。out修飾子で宣言される。
− 仮引数配列。params修飾子で宣言される。
注記 10.6に示すように,ref修飾子及びout修飾子はメソッドの呼出し情報の一部であるが,
params修飾子はそうではない。
17.5.1.1 値仮引数
修飾子を付けずに宣言された仮引数は値仮引数とする。値仮引数は,メソッドの呼出しで与えられた対
316
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
応する実引数から初期値を取得する局所変数に相当する。
仮引数が値仮引数の場合,メソッド呼出しにおいて,それに対応している実引数は,その仮引数型に暗
黙に変換可能(13.1参照)な型の式でなければならない。
メソッドは,値仮引数に新たな値を代入できる。値仮引数への代入によって影響を受けるのは,値仮引
数によって表現される局所的な記憶域だけとする。メソッドの呼出しで与えられる実際の実引数には影響
しない。
17.5.1.2 参照仮引数
ref修飾子をもって宣言された仮引数は参照仮引数とする。参照仮引数は値仮引数とは異なり,新規の
記憶域を生成しない。代わりに,参照仮引数はメソッドの呼出しの実引数として与えられる変数と同じ記
憶域を表現する。
仮引数が参照仮引数の場合,メソッド呼出しにおいて,それに対応している実引数はキーワードrefと,
それに続く,仮引数と同じ型の≪変数参照≫(12.4参照)で構成されなければならない。変数は,参照仮
引数として渡すことができるようになる前に,確実に代入されていなければならない。
参照仮引数は,メソッドの中で常に確実に代入されているとみなされる。
例 次に例を示す。
using System;
class Test
{
static void Swap(ref int x, ref int y) {
int temp = x;
x = y
y = temp;
}
static void Main() {
int i = 1, j = 2;
Swap(ref i, ref j);
Console.WriteLine("i = {0}, j = {1}", i, j);
}
}
この例では,次のように出力される。
i = 2, j = 1
MainのSwapの呼出しにおいて,xはiを表現し,yはjを表現する。すなわち,この呼出し
はiとjとの値を入れ替える効果をもつ。
参照仮引数をとるメソッドでは,複数の名前で同じ記憶域を表現することができる。
例 次に例を示す。
class A
{
string s;
void F(ref string a, ref string b) {
s = "One";
317
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
a = "Two";
b = "Three";
}
void G() {
F(ref s, ref s);
}
}
この例では,G内のFの呼出しはaとbとの両方にsの参照を渡す。すなわち,この呼出しに
おいてs,a及びbはすべて同じ記憶域の場所を参照し,三つの代入すべてがインスタンスフィ
ールドsを変更する。
17.5.1.3 出力仮引数
out修飾子をもって宣言された仮引数は出力仮引数とする。出力仮引数は参照仮引数と同様に,新規の
記憶域を生成しない。代わりに,出力仮引数はメソッドの呼出しの実引数として与えられる変数と同じ記
憶域を表現する。
仮引数が出力仮引数である場合,メソッド呼出しにおいて対応している実引数はキーワードoutと,そ
れに続く,仮引数と同じ型の≪変数参照≫(12.4参照)で構成されなければならない。変数は出力仮引数
として渡すことができるようになる前に確実に代入されている必要はないが,変数が出力仮引数として渡
された呼出しから戻った後で,その変数は確実に代入されているとみなされる。
メソッド内では,出力仮引数は初期状態で局所変数と同様に未代入とみなされ,値が使用される前に確
実に代入されていなければならない。
メソッドの各出力仮引数は,メソッドが戻る以前に確実に代入されていなければならない。
出力仮引数は通常,複数の返却値を生成するメソッドで使用される。
例 次に例を示す。
using System;
class Test
{
static void SplitPath(string path, out string dir, out string name)
{
int i = path.Length;
while (i > 0) {
char ch = path[i ‒ 1];
if (ch == '¥¥' || ch == '/' || ch == ':') break;
i--;
}
dir = path.Substring(0, i);
name = path.Substring(i);
}
static void Main() {
string dir, name;
SplitPath(@"c:¥Windows¥System¥hello.txt", out dir, out
318
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
name);
Console.WriteLine(dir);
Console.WriteLine(name);
}
}
この例では,次のように出力される。
c:¥Windows¥System¥
hello.txt
dir及びname変数は,それらがSplitPathに渡される前に未代入であってもよく,それら
は,その呼出しから戻った後で中では確実に代入されているとみなされることに注意する必要が
ある。
17.5.1.4 仮引数配列
params修飾子をもって宣言される仮引数は仮引数配列とする。仮引数並びに仮引数配列が含まれる場
合,仮引数配列は並びの最後の仮引数でなければならず,一次元配列型でなければならない。
例 型string[]及びstring[][]は仮引数配列の型として使用することはできるが,型
string[,]は使用できない。
params修飾子は,ref修飾子及びout修飾子と組み合わせることはできない。
仮引数配列は,メソッドの呼出しで次の二つのいずれかの方法で実引数を指定できる。
− 仮引数配列に指定された実引数は,その仮引数配列型に暗黙に変換可能(13.1参照)な単一式にでき
る。この場合,仮引数配列は値仮引数と全く同じように機能する。
− 別の方法として,仮引数配列に対して0個以上の実引数を指定した呼出しができる。ここで,個々の
実引数は,その仮引数配列の要素型に暗黙に変換可能(13.1参照)な式とする。この場合,呼出しに
よって,実引数の数に対応した長さをもつ仮引数配列型のインスタンスが生成され,配列インスタン
スの要素が指定の実引数値で初期化され,新しく生成された配列インスタンスが実際の実引数として
使用される。
呼出し内の実引数を可変個にできる場合を除いて,仮引数配列は同じ型の値仮引数(17.5.1.1参照)と正
確に同等とする。
例 次に例を示す。
using System;
class Test
{
static void F(params int[] args) {
Console.Write("Array contains {0} elements:", args.Length);
foreach (int i in args)
Console.Write(" {0}", i);
Console.WriteLine();
}
static void Main() {
int[] arr = {1, 2, 3};
F(arr);
319
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
F(10, 20, 30, 40);
F();
}
}
この例では,次のように出力される。
Array contains 3 elements: 1 2 3
Array contains 4 elements: 10 20 30 40
Array contains 0 elements:
Fの最初の呼出しは,単純に値仮引数として配列arrを渡す。Fint[]Fint[]2番目のFの呼
出しは,指定された要素の値をもった四つの要素をもつint[]を自動的に作成し,値仮引数とし
て配列インスタンスを渡す。同様に,3番目のFの呼出しは0個の要素をもつint[]を生成し,
値仮引数としてそのインスタンスを渡す。2番目及び3番目の呼出しは,次のように記述するの
と全く同じである。
F(new int[] {10, 20, 30, 40});
F(new int[] {});
多重定義解決を実行する場合,仮引数配列をもつメソッドは通常形式又は展開形式(14.4.2.1参照)のい
ずれかで多重定義解決が適用される。メソッドの展開形式を使用できるのは,メソッドの通常形式が適用
可能でない場合で,かつ,展開形式と同じ呼出し情報をもつメソッドが同一型で宣言されていない場合だ
けとする。
例 次に例を示す。
using System;
class Test
{
static void F(params object[] a) {
Console.WriteLine("F(object[])");
}
static void F() {
Console.WriteLine("F()");
}
static void F(object a0, object a1) {
Console.WriteLine("F(object,object)");
}
static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(1, 2, 3, 4);
}
}
320
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
この例では,次のように出力される。
F();
F(object[]);
F(object,object);
F(object[]);
F(object[]);
この例では,仮引数配列をもつメソッドの展開形式のうちの二つが,通常メソッドとして既に
クラスに含まれている。このため,これらの展開形式は多重定義解決の実行時には考慮されず,1
回目及び3回目のメソッドの呼出しでは通常メソッドが選択される。クラスに仮引数配列をもつ
メソッドを宣言する場合,そのメソッドの展開形式の呼出し情報をもつ通常メソッドを含めるこ
ともよくある。そうすることで,仮引数配列をもつメソッドの展開形式が呼び出されるときに実
行される,配列インスタンスの割当てを回避できる。
仮引数配列の型がobject[]の場合,そのメソッドの通常形式と単一のobject仮引数の展開形式との
間に潜在的なあいまいさを生じる。このあいまいさが生じる理由は,object[]がそれ自身object型へ
暗黙に変換可能であることにある。ただし,必要に応じてキャストを挿入することで解決できるので,あ
いまいさがあっても問題はない。
例 次に例を示す。
using System;
class Test
{
static void F(params object[] args) {
foreach (object o in args) {
Console.Write(o.GetType().FullName);
Console.Write(" ");
}
Console.WriteLine();
}
static void Main() {
object[] a = {1, "Hello", 123.456};
object o = a;
F(a);
F((object)a);
F(o);
F((object[])o);
}
}
この例では,次のように出力される。
System.Int32 System.String System.Double
System.Object[]
System.Object[]
321
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
System.Int32 System.String System.Double
Fの最初及び最後の呼出しでは,Fの通常形式が適用できる。なぜなら,実引数型から仮引数
型への暗黙の変換が存在するからである(いずれもobject[]型である。)。このため,多重定義
解決ではFの通常形式が選択され,実引数は通常の値仮引数として渡される。2番目及び3番目
の呼出しでは,Fの通常形式は適用できない。なぜなら,実引数型から仮引数型への暗黙の変換
がないからである(object型はobject[]型へ暗黙に変換できない。)。しかし,Fの展開形式
が適用できる。多重定義解決ではこの展開形式が選択される。結果として,一つの要素の
object[]が呼出しによって生成され,配列の単一要素が指定された実引数値で初期化される(そ
れ自身はobject[]の参照である。)。
17.5.2 静的メソッド及びインスタンスメソッド
メソッド宣言にstatic修飾子を含んでいる場合,そのメソッドを静的メソッドという。static修飾
子が現れていない場合,そのメソッドをインスタンスメソッドという。
静的メソッドは特定のインスタンスでは機能しないので,静的メソッドでthisを参照するとコンパイ
ル時エラーになる。
インスタンスメソッドは指定されたクラスインスタンスに対して処理を行う。そして,そのインスタン
スはthis(14.5.7参照)でアクセスできる。
メソッドが形式E.Mの≪メンバアクセス≫(14.5.4参照)内で参照されている場合,Mが静的メソッド
ならばEはメソッドMをもつ型を示さなければならない。MがインスタンスメソッドならばEはメソッド
Mをもつ型のインスタンスを示さなければならない。
静的メンバとインスタンスメンバとの違いについては,17.2.5に示す。
17.5.3 仮想メソッド
インスタンスメソッド宣言がvirtual修飾子を含んでいる場合,そのメソッドを仮想メソッドという。
virtual修飾子が現れない場合,そのメソッドを非仮想メソッドという。
非仮想メソッドの実装は一定とする。メソッドの呼出し側が,宣言元のクラスのインスタンスであるか,
派生クラスのインスタンスであるかに関係なく,その実装は同じとする。対照的に,仮想メソッドの実装
は,派生クラスによって取り替えられることがある。継承した仮想メソッドの実装を取り替える処理を,
そのメソッドの上書きと呼ぶ(17.5.4参照)。
仮想メソッドの呼出しでは,呼出しが行われるインスタンスの実行時の型が,実際に呼び出されるメソ
ッド実装を決定する。非仮想メソッドの呼出しでは,インスタンスのコンパイル時の型が決定要因である。
正確にいえば,Nという名前のメソッドがコンパイル時の型C及び実行時の型R(ここでRはクラスC又
はCから派生したクラス)をもつインスタンスに対して,実引数並びAをもって呼び出されるとき,その
呼出しは次のように処理される。
− C内で宣言されたメソッド及びCが継承したメソッドの集合から特定のメソッドMを選択するために,
まず,C,N及びAに対して多重定義解決が適用される。これは14.5.5.1に示す。
− 次に,Mが非仮想メソッドであれば,Mが呼び出される。
− そうでなければ,Mは仮想メソッドであり,Rに関するMの最派生実装が呼び出される。
クラス内で宣言された仮想メソッド又は継承した仮想メソッドのそれぞれについて,そのクラスに関す
るメソッドの最派生実装が存在する。クラスRに関する仮想メソッドMの最派生実装は,次のとおりに決
定される。
− RがMのvirtual宣言の導入を含んでいる場合,これがMの最派生実装となる。
322
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− それ以外の場合,RにMのoverrideが含まれていると,これがRに関するMの最派生実装である。
− それ以外の場合,Rに関するMの最派生実装は,Rの直接基底クラスに関するMの最派生実装と同じ
になる。
例 次の例は仮想メソッドと非仮想メソッドとの相違点を例示する。
using System;
class A
{
public void F() { Console.WriteLine("A.F"); }
public virtual void G() { Console.WriteLine("A.G"); }
}
class B: A
{
new public void F() { Console.WriteLine("B.F"); }
public override void G() { Console.WriteLine("B.G"); }
}
class Test
{
static void Main() {
B b = new B();
A a = b;
a.F();
b.F();
a.G();
b.G();
}
}
この例では,Aは非仮想メソッドF及び仮想メソッドGを導入する。クラスBは,new修飾さ
れた非仮想メソッドFを導入する。すなわち,継承したFを隠ぺいする。同時に,継承したメソ
ッドGを上書きする。この例では,次のような出力になる。
A.F
B.F
B.G
B.G
文a.G()はA.GではなくB.Gを呼び出す点に注意が必要である。これは,そのインスタンス
のコンパイル時の型(この場合A)ではなく実行時の型(この場合B)が,呼び出される実際の
メソッド実装を決定するからである。
メソッドは,継承したメソッドを隠ぺいできるので,同じ呼出し情報をもつ複数の仮想メソッドを一つ
のクラスに格納することができる。最派生メソッド以外のすべてが隠ぺいされるので,あいまいさの問題
は発生しない。
例 次に例を示す。
323
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class A
{
public virtual void F() { Console.WriteLine("A.F"); }
}
class B: A
{
public override void F() { Console.WriteLine("B.F"); }
}
class C: B
{
new public virtual void F() { Console.WriteLine("C.F"); }
}
class D: C
{
public override void F() { Console.WriteLine("D.F"); }
}
class Test
{
static void Main() {
D d = new D();
A a = d;
B b = d;
C c = d;
a.F();
b.F();
c.F();
d.F();
}
}
クラスC及びDは同じ呼出し情報の二つの仮想メソッドを含んでいる。一つはAによって導入
され,もう一つはCによって導入されている。Cで導入されたメソッドはAから継承したメソッ
ドを隠ぺいする。すなわち,D内の上書き宣言はCで導入されたメソッドを上書きする。そして,
DはAによって導入されたメソッドを上書きすることはできない。この例は,次のような出力が
生成される。
B.F
B.F
D.F
D.F
そのメソッドが隠ぺいされていないより基底側の派生型を通してDのインスタンスをアクセス
324
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
することによって,隠ぺいされた仮想メソッドを呼び出すことができる点に注意が必要である。
17.5.4 上書きメソッド
インスタンスメソッド宣言にoverride修飾子を含む場合,そのメソッドを上書きメソッドという。上
書きメソッドは同じ呼出し情報の継承した仮想メソッドを上書きする。仮想メソッドの宣言は新しいメソ
ッドを導入するが,上書きメソッドの宣言は,そのメソッドの新しい実装を提供して,既存の継承した仮
想メソッドを特化する。
override宣言によって上書きされたメソッドは,上書きされた基底メソッドという。クラスCで宣言
された上書きメソッドMに対して,上書きされた基底メソッドはCの基底クラスのそれぞれを,Cの直接
基底クラスから開始し,Mと同じ呼出し情報をもったアクセス可能なメソッドが見つかるまで,歴代の直
接基底クラスを検査する。上書きされた基底メソッドを見つけ出す際に,メソッドがpublic,protected
若しくはprotected internalであるか,又はinternalで,かつ,Cと同じプログラム内で宣言され
ていれば,そのメソッドはアクセス可能であるとみなされる。
上書きメソッド宣言に対して次の項目のすべてが真でない限り,コンパイル時エラーになる。
− 上書きされた基底メソッドが上に示した方法で見つけることができる。
− 上書きされた基底メソッドは,仮想メソッド,抽象メソッド又は上書きメソッドのいずれかである。
すなわち,上書きされた基底メソッドは静的メソッド又は非仮想メソッドではない。
− 上書きされた基底メソッドは,封印メソッドではない。
− 上書きメソッド宣言及び上書きされた基底メソッドの返却値の型が(正確に)同じである。
− 上書きメソッド宣言及び上書きされた基底メソッドとが同じ宣言されたアクセス可能性をもつ。すな
わち,上書きメソッド宣言は,仮想メソッドのアクセス可能性を変更できない。
上書きメソッド宣言は≪baseアクセス≫(14.5.8参照)を使って上書きされた基底メソッドにアクセス
できる。
例 次に例を示す。
class A
{
int x;
public virtual void PrintFields() {
Console.WriteLine("x = {0}", x);
}
}
class B: A
{
int y;
public override void PrintFields() {
base.PrintFields();
Console.WriteLine("y = {0}", y);
}
}
B内でのbase.PrintFields()呼出しは,A内で宣言されたPrintFieldsメソッドを呼び
出す。≪baseアクセス≫は仮想呼出しの仕組みを無効にし,単純に基底メソッドを非仮想メソ
325
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ッドとして取り扱う。B内での呼出しが((A)this).PrintFields()と記述されると,A内で宣
言されたものではなく,B内で宣言されたPrintFieldsを再帰的に呼び出すことになる。なぜ
なら,PrintFieldsは仮想メソッドであり,((A)this)の実行時の型がBであることによる。
override修飾子を含めることによってだけ,メソッドが他のメソッドを上書きできる。その他の場合
はすべて,継承したメソッドと同じ呼出し情報をもつメソッドは,単に継承したメソッドを隠ぺいする。
例1 次に例を示す。
class A
{
public virtual void F() {}
}
class B: A
{
public virtual void F() {}
// Warning, hiding inherited
F()
}
B内のFメソッドにはoverride修飾子を含んでいない。それゆえ,A内のFメソッドを上
書きしない。正確にいえば,B内のFメソッドはA内のFメソッドを隠ぺいする。そして,そ
の宣言がnew修飾子を含んでいないので,警告が報告される。
例2 次に例を示す。
class A
{
public virtual void F() {}
}
class B: A
{
new private void F() {}
// Hides A.F within B
}
class C: B
{
public override void F() {}
// Ok, overrides A.F
}
B内のFメソッドはAから継承した仮想Fメソッドを隠ぺいする。B内のnew修飾されたF
は非公開アクセスをもつので,その有効範囲はBのクラス本体だけを含み,Cにまで拡張され
ない。したがって,C内のFの宣言はAから継承したFを上書きすることが許されている。
17.5.5 封印メソッド
インスタンスメソッド宣言にsealed修飾子を含んでいる場合,そのメソッドを封印メソッドという。
封印メソッドは同じ呼出し情報をもった継承した仮想メソッドを上書きする。封印メソッドにoverride
修飾子も指定しなければならない。sealed修飾子は,派生クラスでのメソッドのこれ以上の上書きを抑
止する。
例 次に例を示す。
326
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class A
{
public virtual void F() {
Console.WriteLine("A.F");
}
public virtual void G() {
Console.WriteLine("A.G");
}
}
class B: A
{
public sealed override void F() {
Console.WriteLine("B.F");
}
public override void G() {
Console.WriteLine("B.G");
}
}
class C: B
{
public override void G() {
Console.WriteLine("C.G");
}
}
クラスBは二つの上書きメソッドF及びGを提供する。Fメソッドはsealed修飾子をもち,
Gメソッドはもたない。Bでのsealed修飾子はクラスCでFのこれ以上の上書きを抑止する。
17.5.6 抽象メソッド
インスタンスメソッド宣言にabstract修飾子を含むとき,そのメソッドを抽象メソッドという。抽象
メソッドは暗黙に仮想メソッドでもあるにもかかわらず,virtual修飾子をもつことはできない。
抽象メソッドの宣言は新しい仮想メソッドを導入するが,そのメソッドの実装は提供しない。代わりに,
非抽象派生クラスが,そのメソッドを上書きすることで独自の実装を提供することが要求される。抽象メ
ソッドは実際の実装を提供しないので,抽象メソッドの≪メソッド本体≫を構成するのはセミコロンだけ
である。
抽象メソッド宣言は抽象クラス内でだけ許可される(17.1.1.1参照)。
例 次に例を示す。
public abstract class Shape
{
public abstract void Paint(Graphics g, Rectangle r);
}
327
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public class Ellipse: Shape
{
public override void Paint(Graphics g, Rectangle r) {
g.DrawEllipse(r);
}
}
public class Box: Shape
{
public override void Paint(Graphics g, Rectangle r) {
g.DrawRect(r);
}
}
Shapeクラスは,そのオブジェクト自体を塗りつぶすことができる,幾何学形状オブジェクト
の抽象表記法を定義する。メソッドPaintは,意味のある省略時の実装がないので抽象メソッド
である。Ellipseクラス及びBoxクラスは具体的なShapeの実装である。これらのクラスは非
抽象クラスなので,これらのクラスはPaintメソッドを上書きし,実際の実装を提供しなければ
ならない。
抽象メソッドを参照する≪baseアクセス≫(14.5.8参照)はコンパイル時エラーになる
例 次に例を示す。
abstract class A
{
public abstract void F();
}
class B: A
{
public override void F() {
base.F();
// Error, base.F is abstract
}
}
base.F()が抽象メソッドを参照しているので,この呼出しはコンパイル時エラーになる。
抽象メソッドの宣言は,仮想メソッドを上書きすることを許可する。これによって,抽象クラスの派生
クラス内でメソッドを強制的に再実装させ,そのメソッドの元の(仮想メソッドとしての)実装を使用で
きなくする。
例 次に例を示す。
using System;
class A
{
public virtual void F() {
Console.WriteLine("A.F");
}
328
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
abstract class B: A
{
public abstract override void F();
}
class C: B
{
public override void F() {
Console.WriteLine("C.F");
}
}
クラスAは仮想メソッドを宣言し,クラスBはこのメソッドを抽象メソッドで上書きし,そし
てクラスCは実装を提供するために,その抽象メソッドを上書きする。
17.5.7 外部メソッド
メソッド宣言にextern修飾子が含まれている場合,そのメソッドは外部メソッドという。外部メソッ
ドは外部で実装される。通常,C#以外の言語が使われる。外部メソッド宣言は実際の実装を提供しないの
で,外部メソッドの≪メソッド本体≫を構成するのはセミコロンだけとする。
外部メソッドとの結合を行う機構は実装依存である。
例 次は,extern修飾子と,そのメソッドが実装された外部ライブラリの名前を指定した
DllImport属性とを組み合わせて使用した例を示している。
using System.Text;
using System.Security.Permissions;
using System.Runtime.InteropServices;
class Path
{
[DllImport("kernel32", SetLastError=true)]
static extern bool CreateDirectory(string name, SecurityAttribute
sa);
[DllImport("kernel32", SetLastError=true)]
static extern bool RemoveDirectory(string name);
[DllImport("kernel32", SetLastError=true)]
static extern int GetCurrentDirectory(int bufSize, StringBuilder
buf);
[DllImport("kernel32", SetLastError=true)]
static extern bool SetCurrentDirectory(string name);
}
17.5.8 メソッド本体
メソッド宣言の≪メソッド本体≫は,≪ブロック≫又はセミコロンのいずれかで構成される。
抽象メソッド及び外部メソッド宣言は,メソッドの実装を提供しない。したがって,そのメソッドの本
体はセミコロンだけで構成される。他のメソッドのメソッド本体は,そのメソッドが呼び出されたときに
329
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
実行される文をもつブロック(15.2参照)とする。
メソッドの返却型がvoidの場合,そのメソッド本体にあるreturn文(15.9.4参照)に式を記述でき
ない。voidメソッドのメソッド本体の実行が通常どおり完了する(制御がメソッド本体の末尾を離れる)
と,そのメソッドは呼出し側に返るだけとする。
メソッドの返却型がvoidでない場合,そのメソッド本体にある個々のreturn文は,その返却型に暗
黙に変換できる型の式を記述しなければならない。値を返すメソッドのメソッド本体の終了点は,到達可
能であってはならない。すなわち,値を返すメソッドでは,制御がメソッド本体の末尾に到達して終了す
ることはできない。
例 次に例を示す。
class A
{
public int F() {}
// Error, return value required
public int G() {
return 1;
}
public int H(bool b) {
if (b) {
return 1;
}
else {
return 0;
}
}
}
値を返すFメソッドは,制御がメソッド本体の末尾に到達して終了する可能性があるのでコン
パイル時エラーになる。Gメソッド及びHメソッドは,全実行可能パスが返却値を記述した
return文で終わるので正しい。
17.5.9 メソッド多重定義
メソッド多重定義解決規則は14.4.2で規定する。
17.6 特性
特性は,オブジェクト又はクラスが備える特質へのアクセスを提供するメンバとする。特性の例として
は,文字列の長さ,フォントの大きさ,ウィンドウの説明文,顧客の名前などがある。特性は,フィール
ドを自然な形で拡張したものと考えることができる。いずれも関連する型をもつ名前付きメンバであり,
特性にアクセスする構文もフィールドと同じとする。ただし,フィールドとは異なり,特性は記憶域を表
さない。代わりに,特性には,値の読込み時又は書出し時に実行される文を指定するアクセス子がある。
したがって,特性はオブジェクトの特質の読み書きに動作を関連付けるための機構を提供し,更に,その
オブジェクトの特質の値を計算できる。
特性は≪特性宣言≫を使って宣言される。
≪特性宣言≫:
≪属性群≫opt ≪特性修飾子群≫opt ≪型≫ ≪メンバ名≫ { ≪アクセス子宣言
330
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
群≫ }
≪特性修飾子群≫:
≪特性修飾子≫
≪特性修飾子群≫ ≪特性修飾子≫
≪特性修飾子≫:
new
public
protected
internal
private
static
virtual
sealed
override
abstract
extern
≪特性宣言≫は,≪属性≫の集合(箇条24参照)及び四つのアクセス修飾子(17.2.3参照)の有効な組
合せ,修飾子new(17.2.2参照),static(17.6.1参照),virtual(17.5.3及び17.6.3参照),override
(17.5.4及び17.6.3参照),sealed(17.5.5参照),abstract(17.5.6及び17.6.3参照)及びexternを
含むことができる。
特性宣言には,修飾子の有効な組合せに関してメソッド宣言(17.5参照)と同じ規則が適用される。
特性宣言の≪型≫は,その宣言によって導入される特性の型を指定し,≪メンバ名≫は特性の名前を指
定する。特性が明示的なインタフェースメンバ実装ではない場合,≪メンバ名≫は単に≪識別子≫とする。
明示的なインタフェースメンバ実装(20.4.1参照)ならば,≪メンバ名≫は,≪インタフェース型≫とそ
れに続く“.”及び≪識別子≫から構成される。
特性の≪型≫は,少なくとも特性(10.5.4参照)自身と同じアクセス可能性をもたなければならない。
≪アクセス子宣言群≫は,特性のアクセス子(17.6.2参照)を宣言するが,字句“{”及び“}”によっ
て囲まれなければならない。アクセス子は,特性の読込み及び書出しに関連付ける実行可能な文を指定す
る。
特性にアクセスするための構文がフィールドの構文と同じであるとしても,特性は変数として分類され
ない。したがって,実引数ref又はoutとして特性を渡すことはできない。
特性宣言が修飾子externを含む場合は,その特性は外部特性であると呼ばれる。外部特性宣言は実際
の実装をもたないので,各≪アクセス子宣言群≫は,セミコロンから構成される。
17.6.1 静的特性及びインスタンス特性
特性宣言が修飾子staticを含む場合は,その特性は静的特性であると呼ばれる。修飾子staticが存
在しない場合,その特性はインスタンス特性であると呼ばれる。
静的特性は特定のインスタンスと関連付けられていないので,静的特性のアクセス子でthisを参照す
るとコンパイル時エラーになる。
インスタンス特性は特定のクラスのインスタンスと関連付けられているので,そのインスタンスをその
特性のアクセス子の中でthis(14.5.7参照)としてアクセスできる。
331
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
静的メンバとインスタンスメンバとの間の相違点は,17.2.5でより詳しく規定する。
17.6.2 アクセス子
特性の≪アクセス子宣言群≫は,その特性の読込み及び書出しに関連付ける実行可能な文を指定する。
≪アクセス子宣言群≫:
≪getアクセス子宣言≫ ≪setアクセス子宣言≫opt
≪setアクセス子宣言≫ ≪getアクセス子宣言≫opt
≪getアクセス子宣言≫:
≪属性群≫opt ≪アクセス子修飾子≫opt get ≪アクセス子本体≫
≪setアクセス子宣言≫:
≪属性群≫opt ≪アクセス子修飾子≫opt set ≪アクセス子本体≫
≪アクセス子修飾子≫:
protected
internal
private
protected internal
internal protected
≪アクセス子本体≫:
≪ブロック≫
;
アクセス子の宣言は,≪getアクセス子宣言≫と≪setアクセス子宣言≫のいずれか一方又は両方で構
成される。各アクセス子宣言は,字句get又は字句setに続く≪アクセス子本体≫から構成される。
abstract特性及びextern特性に対しては,指定された各アクセス子の≪アクセス子本体≫は,単にセ
ミコロンとする。非abstract特性及び非extern特性のアクセス子の場合,≪アクセス子本体≫は,対
応するアクセス子の呼出し時に実行される文を指定する≪ブロック≫とする。
アクセス子getは,特性型の返却値をもつ引数なしのメソッドに対応する。代入先である場合を除いて,
式の中で特性が参照されたときに,その特性のアクセス子getがその特性の値を計算するために呼び出さ
れる(14.1.1参照)。アクセス子getの本体は,17.5.8で述べられている値を返すメソッドの規則に適合し
なければならない。特に,アクセス子getの本体の中のすべてのreturn文は,特性型に暗黙に変換でき
る式を指定しなければならない。さらに,アクセス子getの終了点に到達可能であってはならない。
アクセス子setは,特性型の単一値の仮引数及び返却値の型voidをもつメソッドに対応する。アクセ
ス子setの暗黙の仮引数は,常にvalueという名前とする。特性が,代入(14.13参照)先又は++若しく
は--(14.5.9及び14.6.5参照)の演算対象として参照されるときに,アクセス子setは新しい値(14.14.1
参照)を与える実引数を伴って呼び出される。アクセス子setの本体は,17.5.8で述べるvoidメソッド
の規則に適合しなければならない。特に,アクセス子set本体の中のreturn文に式を指定できない。ア
クセス子setは暗黙にvalueという名前の仮引数をもつので,その名前をもつアクセス子setの中の局
所変数宣言又は局所定数宣言はコンパイル時エラーになる。
アクセス子get及びsetの有無を基にして,特性は次のように分類される。
− アクセス子get及びsetの両方を含む特性は,読み書き可能特性であるという。
− アクセス子getだけをもつ特性は,読込み専用特性という。読込み専用特性を代入先にすると,コン
332
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
パイル時エラーになる。
− アクセス子setだけをもつ特性は,書出し専用特性という。単純代入の目標として以外に,式の中で
書出し専用特性を参照するとコンパイル時エラーになる。
注記 前置及び後置の演算子++及び--は,これらの演算対象の新しい値を書き出す前に古い値を
読み込むので,書出し専用特性に適用できない。
≪アクセス子修飾子≫の使用は,次の制限を受ける。
− ≪アクセス子修飾子≫は,インタフェース又は明示的インタフェースメンバ実装においては使用でき
ない。
− 修飾子overrideをもたない特性又は添字子では,≪アクセス子修飾子≫は,その特性又は添字子が
アクセス子get及びsetの両方をもつときにだけ許され,しかも,そのいずれかのアクセス子につ
いてだけ許される。
− 修飾子overrideをもつ特性又は添字子では,アクセス子の≪アクセス子修飾子≫は,上書きされる
アクセス子の≪アクセス子修飾子≫(がもしあれば)に合致しなければならない。
− ≪アクセス子修飾子≫は,特性又は添字子そのものの宣言されたアクセス可能性よりも厳密に制限的
なアクセス可能性を宣言しなければならない。具体的には次となる。
・ 特性又は添字子の宣言されたアクセス可能性がpublicならば,任意のアクセス子修飾子を使用で
きる。
・ 特性又は添字子の宣言されたアクセス可能性がprotected internalならば,アクセス子修飾子
は,internal,protected又はprivateとなる。
・ 特性又は添字子の宣言されたアクセス可能性がinternal又はprotectedならば,アクセス子修
飾子は,privateでなければならない。
・ 特性又は添字子の宣言されたアクセス可能性がprivateならば,アクセス子修飾子を使ってはな
らない。
アクセス子が≪アクセス子修飾子≫をもつならば,アクセス子のアクセス可能領域(10.5.2参照)は,
≪アクセス子修飾子≫の宣言されたアクセス可能性を用いて決定される。アクセス子が≪アクセス子修飾
子≫をもたないならば,アクセス子のアクセス可能領域は,特性又は添字子の宣言されたアクセス可能性
から決定される。
例
public class Button: Control
{
private string caption;
public string Caption {
get {
return caption;
}
set {
if (caption != value) {
caption = value;
Repaint();
}
333
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
public override void Paint(Graphics g, Rectangle r) {
// Painting code goes here
}
}
このコードでは, Controlを実装又は継承したクラスButtonは,公開特性Captionを宣
言する。特性Captionのアクセス子getは,非公開フィールドcaptionの中に格納された文
字列を返す。アクセス子setは,新しい値が現在の値と異なるかどうかを検査する。異なる場合
は,新しい値を格納し,処理対象のButtonクラスのインスタンスを再描画する。多くの場合,
特性は上記のパターンに従う。アクセス子getは非公開フィールドに格納された値を返すだけと
する。アクセス子setは非公開フィールドを変更し,オブジェクトの状態を完全に更新するため
に必要な追加の動作を実行する。
次は,上で定義したクラスButtonを利用した特性Captionの使用例である。
Button okButton = new Button();
okButton.Caption = "OK";
// Invokes set accessor
string s = okButton.Caption;
// Invokes get accessor
ここでアクセス子setは,値をその特性に代入することで呼び出され,アクセス子getは式
の中でその特性を参照することで呼び出される。
特性アクセス子get及びsetは,別個のメンバではない。特性アクセス子は別々に宣言できない。
例
class A
{
private string name;
public string Name {
// Error, duplicate member name
get { return name; }
}
public string Name {
// Error, duplicate member name
set { name = value; }
}
}
この例は,一つの読み書き可能特性を宣言していない。読込み専用と書出し専用の二つの特性
を同じ名前で宣言している。同一クラスの中で宣言された二つのメンバは同じ名前をもつことは
できないので,この例はコンパイル時エラーになる。
派生クラスが継承された特性と同じ名前で特性を宣言する場合,派生特性は継承された特性を読込み及
び書出しの両方の点で隠ぺいする。
例
class A
{
public int P {
334
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
set {…}
}
}
class B: A
{
new public int P {
get {…}
}
}
上のコードでは,Bの中の特性Pは,読込み及び書出しについてAの中の特性Pを隠ぺいする。
したがって,次の文のようになる。
B b = new B();
b.P = 1;
// Error, B.P is read-only
((A)b).P = 1;
// Ok, reference to A.P
b.Pへの代入は,Bの中の読込み専用特性PがAの中の書出し専用特性Pを隠ぺいするので,
コンパイル時エラーになる。しかし,隠ぺいされた特性Pにアクセスするためにキャストを使用
できることに注意する。
公開フィールドとは異なり,特性はオブジェクトの内部状態とその公開インタフェースとを分離する。
例 次を考える。
class Label
{
private int x, y;
private string caption;
public Label(int x, int y, string caption) {
this.x = x;
this.y = y;
this.caption = caption;
}
public int X {
get { return x; }
}
public int Y {
get { return y; }
}
public Point Location {
get { return new Point(x, y); }
}
public string Caption {
get { return caption; }
}
335
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
ここで,このクラスLabelは,その位置を格納するために二つのintフィールドx及びyを
使用する。その位置は,特性X及びYとして,並びに型Pointの特性Locationとしての両方
で公に開示されている。Labelの将来の版で,位置を内部的にはPointとして格納するほうが
便利な場合は,クラスの公開インタフェースに影響を与えることなく次のように変更できる。
class Label
{
private Point location;
private string caption;
public Label(int x, int y, string caption) {
this.location = new Point(x, y);
this.caption = caption;
}
public int X {
get { return location.x; }
}
public int Y {
get { return location.y; }
}
public Point Location {
get { return location; }
}
public string Caption {
get { return caption; }
}
}
もしも,フィールドx及びyが公開読込み専用フィールドとして宣言されていたならば,クラ
スLabelに上のような変更を加えることはできない。
注記 特性を通して状態を開示することは,フィールドを直接開示することと同様に効果的な場合が
ある。具体的には,特性が,非仮想で少量のコードしか含んでいない場合は,実行環境によっ
てアクセス子への呼出しをアクセス子の実際のコードで置き換えられることがある。この処理
は,インライン展開として知られており,それは特性アクセスをフィールドアクセスと同じく
らい効率的にしつつ,特性の柔軟性を維持する。
例 アクセス子getを呼び出すことは,概念的にフィールドの値を読み込むことと等価なので,観測
可能な副作用をもつアクセス子getは悪いプログラムの書き方と考えられる。次に例を示す。
class Counter
{
private int next;
public int Next {
get { return next++; }
336
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
特性Nextの値は,特性がこれまでにアクセスされた回数に依存する。したがって,その特性
にアクセスすることは観測可能な副作用を生む。この特性は代わりにメソッドとして実装するの
がよい。
注記 アクセス子getが“副作用をもたない”という規約は,アクセス子getがフィールドに格納
された値を返却するだけの常に単純なものにするのがよいということを意味しない。実際,ア
クセス子getはしばしば,複数のフィールドにアクセスしたり,メソッドを呼び出したりする
ことで,特性の値を計算する。しかし,適切に設計されたアクセス子getは,オブジェクトの
状態に観測可能な変更を引き起こさない。
特性を使うと,資源が最初に参照されるまで資源の初期化を遅らせることができる。
例
using System.IO;
public class Console
{
private static TextReader reader;
private static TextWriter writer;
private static TextWriter error;
public static TextReader In {
get {
if (reader == null) {
reader = new
StreamReader(Console.OpenStandardInput());
}
return reader;
}
}
public static TextWriter Out {
get {
if (writer == null) {
writer = new
StreamWriter(Console.OpenStandardOutput());
}
return writer;
}
}
public static TextWriter Error {
get {
if (error == null) {
error = new
337
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
StreamWriter(Console.OpenStandardError());
}
return error;
}
}
…
}
クラスConsoleは,それぞれ標準入力装置,標準出力装置,及び標準エラー装置を表す三つ
の特性In,Out及びErrorを含む。これらのメンバを特性として開示することで,クラス
Consoleはそれらが実際に使われるまで初期化を遅らせることができる。例えば,次の例のよう
に特性Outが最初に参照されたときに,その出力装置の基礎となるTextWriterが作成される。
Console.Out.WriteLine("hello, world");
しかし,もしアプリケーションが特性In及びErrorへの参照を行わないならば,それらの装
置に対してはオブジェクトが作成されない。
≪アクセス子修飾子≫の存在は,メンバ検索(14.3参照)又は多重定義解決(14.4.2参照)に絶対に影
響しない。特性又は添字子の修飾子は,どの特性又は添字子が束縛されるかを常にアクセス文脈とは関係
なく決定する。
特定の特性又は添字子が選択されると,含まれる特定のアクセス子のアクセス可能領域が,その使用が
正当かどうかの次の決定に用いられる。
− 値としての使用ならば(14.1.1参照),アクセス子getが存在し,アクセス可能でなければならない。
− 単純代入の対象としての使用ならば(14.14.1参照),アクセス子setが存在し,アクセス可能でなけ
ればならない。
− 複合代入(14.14.2参照)又は演算子++若しくは演算子--(14.5.9及び14.6.5参照)の対象としての使
用ならば,アクセス子get及びsetの両方が存在し,アクセス可能でなければならない。
例 次の例では,特性A.Textは,アクセス子setだけが呼び出される文脈においてすらB.Text
によって隠ぺいされている。対照的に,特性B.Countは,クラスMにはアクセス可能では
なくて,その代わりにアクセス可能特性A.Countが用いられる。
class A
{
public string Text {
get { return "hello"; }
set { }
}
public int Count {
get { return 5; }
set { }
}
}
class B: A
{
338
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
private string text = "goodbye";
private int count = 0;
new public string Text {
get { return text; }
protected set { text = value; }
}
new protected int Count {
get { return count; }
set { count = value; }
}
}
class M
{
static void Main() {
B b = new B();
b.Count = 12;
// Calls A.Count set accessor
int i = b.Count;
// Calls A.Count get accessor
b.Text = "howdy";
// Error, B.Text set accessor not
accessible
string s = b.Text;
// Calls B.Text get accessor
}
}
インタフェースを実装するのに用いられるアクセス子は,≪アクセス子修飾子≫をもてない。
しかし,一つのアクセス子がそのインタフェースの実装に用いられさえすれば,他のアクセス
子は,≪アクセス子修飾子≫付きで宣言できる。
例
public interface I
{
string Prop { get; }
}
public class C: I
{
public string Prop {
get { return "April"; } // Must not have a modifier here
internal set {…}
// Ok, because I.Prop has no set
accessor
}
}
17.6.3 virtual,sealed,override及びabstractのアクセス子
特性宣言virtualは,その特性アクセス子が仮想であることを指定する。修飾子virtualは,特性の
339
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
非公開ではないすべてのアクセス子に適用される。仮想特性のアクセス子が非公開の≪アクセス子修飾子
≫をもつとき,その非公開のアクセス子は暗黙では仮想ではない。
特性宣言abstractは,その特性アクセス子が仮想アクセス子であり,アクセス子が実際には実装され
ないことを指定する。特性を上書きしてアクセス子の独自の実装を提供するには,非抽象派生クラスを必
要とする。抽象特性宣言のアクセス子は,実際の実装を提供しないので,≪アクセス子本体≫を構成する
のはセミコロンだけとする。抽象特性メンバは,非公開のアクセス子をもってはならない。
修飾子abstract及びoverrideの両方を含む特性宣言は,その特性が抽象であることを指定し,基
底特性を上書きする。この場合の特性アクセス子も抽象アクセス子とする。
抽象特性宣言は,抽象クラス(17.1.1.1参照)の中でだけ許可される。継承された仮想特性アクセス子は
指令overrideを指定する特性宣言を含めることによって派生クラスの中で上書きすることができる。こ
れは上書きする特性宣言と呼ばれる。上書きする特性宣言では新規特性は宣言されない。代わりに,既存
の仮想特性アクセス子に対する実装を特化するだけとする。
上書きする特性宣言では,継承された特性と全く同じアクセス可能性修飾子,型及び名前を指定する必
要がある。継承された特性が一つのアクセス子だけを保有する場合(すなわち,継承された特性が読込み
専用又は書出し専用である場合),上書きする特性にはそのアクセス子だけを含める必要がある。継承され
た特性が二つのアクセス子を保有する場合(すなわち,継承された特性が読み書き可能である場合),上書
きする特性には一つのアクセス子を含めることも,二つのアクセス子を含めることもできる。継承された
アクセス子の一つが上書きする型に対してアクセス可能ではない場合,上書きする特性メンバはそのアク
セス子を含んではならない。さらに,そのアクセス子の宣言されたアクセス可能性は,上書きされるアク
セス子のアクセス可能性と一致しなければならない。
例
public class B
{
public virtual int P {
protected set {…}
get {…}
}
public virtual int Q {
private set {…}
// not virtual
get {…}
}
}
public class D: B
{
public override int P {
protected set {…}
// Must specify protected here
get {…}
// Must not have a modifier here
}
public override int Q {
private set {…}
// Error: inherited set is not
340
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
accessible
get {…}
// Must not have a modifier here
}
}
上書きする特性宣言は,修飾子sealedを含んでもよい。この修飾子を使用すると,派生クラスで特性
をこれ以上上書きしないように封印できる。この封印特性アクセス子も封印される。
宣言と呼出しとの構文上の違いを除けば,仮想,封印,上書き及び抽象の各アクセス子は,仮想,封印,
上書き及び抽象の各メソッドと全く同じように動作する。具体的には17.5.3,17.5.4,17.5.5,及び17.5.6
で述べられている規則が,アクセス子が次の対応する諸形式のメソッドであるかのように適用される。
アクセス子getは,特性の型の返却値及び包含する特性と同じ修飾子をもつ引数なしのメソッドに対応
する。
アクセス子setは,特性の型の単一値の仮引数,返却値の型void,及びそれを含んでいる特性と同じ
修飾子をもつメソッドに対応する。
例
abstract class A
{
int y;
public virtual int X {
get { return 0; }
}
public virtual int Y {
get { return y; }
set { y = value; }
}
public abstract int Z { get; set; }
}
このコードでは,Xは仮想読込み専用特性,Yは仮想読書き可能特性,及びZは抽象読書き可
能特性である。Zは抽象なので,それを含んでいるクラスAもまた抽象と宣言されなければなら
ない。
Aから派生するクラスを次に示す。
class B: A
{
int z;
public override int X {
get { return base.X + 1; }
}
public override int Y {
set { base.Y = value < 0? 0: value; }
}
public override int Z {
341
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
get { return z; }
set { z = value; }
}
}
ここで,X,Y及びZの宣言は,上書きする特性宣言である。各特性宣言では,対応する継承
された特性のアクセス可能性修飾子,型,及び名前が正確に一致している。Xのアクセス子get
及びYのアクセス子setは,その継承されたアクセス子にアクセスするためにキーワードbase
を使用する。Zの宣言は,両方の抽象アクセス子を上書きする。したがって,Bの中に未解決の
抽象関数メンバは存在せず,Bは非抽象クラスであることが許される。
17.7 イベント
イベントは,オブジェクト及びクラスが通知を発行できるようにするためのメンバとする。クライアン
トは,イベントハンドラを提供することによって,イベントに実行可能コードを添付できる。
イベントは,≪イベント宣言≫を使って宣言される。
≪イベント宣言≫:
≪属性群≫opt ≪イベント修飾子群≫opt event ≪型≫ ≪変数宣言子群≫ ;
≪属性群≫opt ≪イベント修飾子群≫opt event ≪型≫ ≪メンバ名≫ { ≪イ
ベントアクセス子宣言群≫ }
≪イベント修飾子群≫:
≪イベント修飾子≫
≪イベント修飾子群≫ ≪イベント修飾子≫
≪イベント修飾子≫:
new
public
protected
internal
private
static
virtual
sealed
override
abstract
extern
≪イベントアクセス子宣言群≫:
≪addアクセス子宣言≫ ≪removeアクセス子宣言≫
≪removeアクセス子宣言≫ ≪addアクセス子宣言≫
≪addアクセス子宣言≫:
≪属性群≫opt add ≪ブロック≫
≪removeアクセス子宣言≫:
≪属性群≫opt remove ≪ブロック≫
≪イベント宣言≫は,≪属性≫(箇条24参照)の集合及び四つのアクセス修飾子(17.2.3参照),修飾
342
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
子new(17.2.2参照),static(17.5.2及び17.7.3参照),virtual(17.5.3及び17.7.4参照),override
(17.5.4及び17.7.4参照),sealed(17.5.5),abstract(17.5.6及び17.7.4参照)及びexternの有効
な組合せを含むことができる。
イベント宣言は,修飾子の有効な組合せに関して,メソッド宣言(17.5参照)と同じ規則に従う。
イベント宣言の≪型≫は,≪委譲型≫(11.2参照)でなければならず,≪委譲型≫は,少なくともその
イベント自身(10.5.4参照)と同じアクセス可能性でなければならない。
イベント宣言は≪イベントアクセス子宣言群≫を含むことができる。しかし,もしそれが含まれていな
いならば,非外部,非抽象イベントに対してコンパイラはそれらを自動的(17.7.1参照)に提供しなけれ
ばならない。外部イベントに対しては,アクセス子は外部から提供される。
≪イベントアクセス子宣言群≫を省略するイベント宣言は,≪変数宣言子群≫のそれぞれに一つずつイ
ベントを定義する。属性及び修飾子は,この≪イベント宣言≫で宣言されたすべてのメンバに適用される。
≪イベント宣言≫に修飾子abstract及び≪変数初期化子≫群又は波括弧でくく(括)られた≪イベン
トアクセス子宣言群≫を同時に含めると,コンパイル時エラーになる。
イベント宣言が修飾子externを含むとき,そのイベントは外部イベントと呼ぶ。外部イベント宣言は
実際の実装を提供しないので,修飾子extern及び≪イベントアクセス子宣言群≫の両方を含むことは(コ
ンパイル時)エラーとする。
イベントは,演算子+=及び‒=(14.14.3参照)の左辺の演算対象として使用できる。演算子+=はイベン
トにイベントハンドラを添付する場合に使用し,演算子-=はイベントからイベントハンドラを削除する場
合に使用する。イベントのアクセス修飾子は,イベントでの演算が許可される文脈を制御する。
イベントを宣言する型の外側でコードによってイベントに対して許可されている演算は,+=及び-=だけ
に限られる。したがって,そのコードはイベントハンドラを追加及び削除できるが,イベントに内在する
イベントハンドラの並びを直接取得又は変更することはできない。x += y又はx ‒= yの形式の演算に
おいて,xがイベントであり,その参照がxの宣言を含む型の外側で行われるとき,その演算の結果は型
void(代入の場合の,xの値でxの型をもつのとは異なる。)をもつ。この規則によって,外部コードに
対して,イベントに保持されている委譲の間接検査を禁止する。
例 次の例は,イベントハンドラがクラスButtonのインスタンスにどのように添付されるかを示す。
public delegate void EventHandler(object sender, EventArgs e);
public class Button: Control
{
public event EventHandler Click;
}
public class LoginDialog: Form
{
Button OkButton;
Button CancelButton;
public LoginDialog() {
OkButton = new Button(…);
OkButton.Click += OkButtonClick;
CancelButton = new Button(…);
CancelButton.Click += CancelButtonClick;
343
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
void OkButtonClick(object sender, EventArgs e) {
// Handle OkButton.Click event
}
void CancelButtonClick(object sender, EventArgs e) {
// Handle CancelButton.Click event
}
}
ここで,インスタンス構築子LoginDialogは,二つのButtonのインスタンスを生成し,イ
ベントClickにイベントハンドラを添付する。
17.7.1 フィールドのように使用できるイベント
イベント宣言を含むクラス又は構造体のプログラムテキスト内では,特定のイベントをフィールドのよ
うに使用できる。このように使用するためには,イベントはabstract又はexternであってはならず,
≪イベントアクセス子宣言群≫を明示的に含んではならない。これに該当するイベントは,フィールドを
許可するすべての文脈で使用できる。そのフィールドは,そのイベントに追加されたイベントハンドラの
並びを参照する委譲(22.参照)を含む。イベントハンドラが何も追加されていないと,そのフィールドは
nullをもつ。
例
public delegate void EventHandler(object sender, EventArgs e);
public class Button: Control
{
public event EventHandler Click;
protected void OnClick(EventArgs e) {
EventHandler toRaise = Click;
if (toRaise != null)
toRaise(this, e);
}
public void Reset() {
Click = null;
}
}
このコードでは,ClickがクラスButtonの中のフィールドとして使用される。例が示すよう
に,このフィールドは委譲の呼出し式で検査,変更及び使用できる。クラスButtonの中のメソ
ッドOnClickは,そのイベントClickを“発生”させる。イベント発生の表記は,イベントに
よって表現される委譲の呼出しと全く同じとする。このため,イベント発生用の特定な言語構築
要素はない。委譲の呼出しは,その委譲が非nullであり,及びスレッド安全性を保障するため
に検査が局所コピーに対して行われていることを確認する検査の後に行われる。
クラスButtonの宣言の外側では,メンバClickは次に示すように演算子+=及び‒=の左辺で
しか使用できない。
b.Click += new EventHandler(…);
344
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
これは,イベントClickの呼出し並びに委譲を追加する。
b.Click ‒= new EventHandler(…);
これはイベントClickの呼出し並びから委譲を削除する。
フィールドのように使用できるイベントをコンパイルすると,コンパイラは委譲を保持する記憶域を自
動的に生成し,委譲フィールドのイベントハンドラを追加又は削除するイベントアクセス子を生成する。
スレッド安全とするために,
− クラスのすべてのインスタンスイベントに対する追加演算又は削除演算は,それを含むオブジェクト
(this) に一意に関連付けられているオブジェクトに対するロック(15.12参照)を保持している間に
行わねばならない。
注記 例えば,一意なオブジェクトは,それを含むオブジェクト自身又は隠ぺいされたインスタ
ンスフィールドであってもよい。
− クラスのすべての静的イベントに対する追加演算及び削除演算は,それを含むクラスに一意に関連付
けられているクラスに対するロック(15.12参照)を保持している間に行わねばならない。
注記 例えば,一意なオブジェクトは,それを含むクラス自身又は隠ぺいされた静的フィールド
に対する型オブジェクト(14.5.11参照)であってもよい。
注記 したがって,次の形式のインスタンスイベント宣言を考える。
class X
{
public event D Ev1;
public event D Ev2;
}
これをコンパイルすると,次になる。
class X
{
public event D Ev1 {
add {
lock(this) { ̲̲ev1 = ̲̲ev1 + value; }
}
remove {
lock(this) { ̲̲ev1 = ̲̲ev1 - value; }
}
public event D Ev2 {
add {
lock(this) { ̲̲ev2 = ̲̲ev2 + value; }
}
remove {
lock(this) { ̲̲ev2 = ̲̲ev2 - value; }
}
}
private D ̲̲ev1; // field to hold a delegate
345
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
private D ̲̲ev2; // field to hold a delegate
}
又は,次になる。
class X
{
public event D Ev1 {
add {
lock(̲̲key) { ̲̲ev1 = ̲̲ev1 + value; }
}
remove {
lock(̲̲key) { ̲̲ev1 = ̲̲ev1 - value; }
}
}
public event D Ev2 {
add {
lock(̲̲key) { ̲̲ev2 = ̲̲ev2 + value; }
}
remove {
lock(̲̲key) { ̲̲ev2 = ̲̲ev2 - value; }
}
}
private D ̲̲ev1; // field to hold a delegate
private D ̲̲ev2; // field to hold a delegate
private readonly object ̲̲key = new object();
}
同様に,次の形式の静的イベント宣言を考える。
class X
{
public static event D Ev1;
public static event D Ev2;
}
この宣言をコンパイルすると,次のようになる。
class X
{
public static event D Ev1
{
add {
lock(typeof(X)) { ̲̲ev1 = ̲̲ev1 + value; }
}
remove {
346
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
lock(typeof(X)) { ̲̲ev1 = ̲̲ev1 - value; }
}
}
public static event D Ev2
{
add {
lock(typeof(X)) { ̲̲ev2 = ̲̲ev2 + value; }
}
remove {
lock(typeof(X)) { ̲̲ev2 = ̲̲ev2 - value; }
}
}
private static D ̲̲ev1; // field to hold a delegate
private static D ̲̲ev2; // field to hold a delegate
}
又は,次になる。
class X
{
public static event D Ev1
{
add {
lock(̲̲key) { ̲̲ev1 = ̲̲ev1 + value; }
}
remove {
lock(̲̲key) { ̲̲ev1 = ̲̲ev1 - value; }
}
}
public static event D Ev2
{
add {
lock(̲̲key) { ̲̲ev2 = ̲̲ev2 + value; }
}
remove {
lock(̲̲key) { ̲̲ev2 = ̲̲ev2 - value; }
}
}
private static D ̲̲ev1; // field to hold a delegate
private static D ̲̲ev2; // field to hold a delegate
private static readonly object ̲̲key = new object();
}
347
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
クラスXの内部では,Ev1及びEv2に対する参照は,代わりに隠ぺいされたフィールド̲̲ev1
及び̲̲ev2を参照するようにコンパイルされる。この名前“̲̲ev1”及び“̲̲ev2”は適宜に
付けたものであり,隠ぺいされたフィールドは任意の名前をもつことも,全く名前をもたない
こともできる。
注記 構造体型の中に含まれるフィールドのように使用できるイベントへのアクセスは,スレッ
ド安全ではない。
17.7.2 イベントアクセス子
注記 17.7.1のButtonの例のように,イベント宣言は,通常は≪イベントアクセス子宣言群≫を省
略する。この宣言を省略する状況としては,1イベントにつき1フィールドの記憶域の費用が
受け入れられない場合がある。その場合は,クラスに≪イベントアクセス子宣言群≫を含めて,
イベントハンドラの並びを格納する専用の機構を使用できる。同様に,外部資源へのアクセス
を必要とする場合には,イベントアクセス子を用いて,これらの資源を管理できる。
イベントの≪イベントアクセス子宣言群≫は,イベントハンドラの追加及び削除に関連付ける実行可能
な文を指定する。
アクセス子の宣言は,≪addアクセス子宣言≫及び≪removeアクセス子宣言≫で構成される。各アク
セス子の宣言は,その字句add又はremoveと,それに続く≪ブロック≫で構成される。≪addアクセス
子宣言≫の≪ブロック≫は,イベントハンドラが追加されたときに実行するための文を指定し,≪remove
アクセス子宣言≫の≪ブロック≫は,イベントハンドラが削除されたときに実行するための文を指定する。
各≪addアクセス子宣言≫及び≪removeアクセス子宣言≫は,イベント型の単一の値の仮引数及び返
却値の型voidをもつメソッドに相当する。イベントアクセス子の暗黙の仮引数はvalueと命名される。
イベントをイベント代入で使用する場合は,適切なイベントアクセス子が使用される。具体的には,代入
演算子が+=ならば,addアクセス子が使用され,代入演算子が‒=ならば,removeアクセス子が使用され
る。いずれの場合も,代入演算子の右側の演算対象は,イベントアクセス子の実引数として使用される。
≪addアクセス子宣言≫及び≪removeアクセス子宣言≫のブロックは,17.5.8で記述されているvoid
メソッドの規則に適合しなければならない。特に,そのようなブロックの中のreturn文は,式を指定す
ることは許されない。
イベントアクセス子はvalueという名前の仮引数を暗黙に保有するので,イベントアクセス子で宣言さ
れる局所変数又は定数にその名前があるとコンパイル時エラーになる。
例
class Control: Component
{
// Unique keys for events
static readonly object mouseDownEventKey = new object();
static readonly object mouseUpEventKey = new object();
// Return event handler associated with key
protected Delegate GetEventHandler(object key) {…}
// Add event handler associated with key
protected void AddEventHandler(object key, Delegate handler) {…}
// Remove event handler associated with key
protected void RemoveEventHandler(object key, Delegate handler) {…}
348
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
// MouseDown event
public event MouseEventHandler MouseDown {
add { AddEventHandler(mouseDownEventKey, value); }
remove { RemoveEventHandler(mouseDownEventKey, value); }
}
// MouseUp event
public event MouseEventHandler MouseUp {
add { AddEventHandler(mouseUpEventKey, value); }
remove { RemoveEventHandler(mouseUpEventKey, value); }
}
// Invoke the MouseUp event
protected void OnMouseUp(MouseEventArgs args) {
MouseEventHandler handler;
handler =
(MouseEventHandler)GetEventHandler(mouseUpEventKey);
if (handler != null)
handler(this, args);
}
}
このコードでは,クラスControlは,イベントに対する内部記憶機構を実装する。メソッド
AddEventHandlerは委譲値をキーと関連付け,メソッドGetEventHandlerは現在キーに関
連付けられている委譲を返し,メソッドRemoveEventHandlerは指定されたイベントに対する
イベントハンドラとしての委譲を削除する。たぶん,その基礎とする記憶機構はキーに委譲値
nullを関連付けても費用がかからないように設計されているので,ハンドラのないイベントは
記憶域を消費しない。
17.7.3 静的イベント及びインスタンスイベント
イベント宣言が修飾子staticを含むときは,そのイベントは静的イベントであると呼ばれる。修飾子
staticが存在しないときは,そのイベントはインスタンスイベントであると呼ばれる。
静的イベントは特定のインスタンスと関連付けられていないので,静的イベントアクセス子でthisを
参照するとコンパイル時エラーになる。
インスタンスイベントは,与えられたクラスのインスタンスに関連付けられ,このインスタンスは,そ
のイベントアクセス子の中でthis(14.5.7参照)としてアクセスできる。
イベントがE.M形式の≪メンバアクセス≫(14.5.4参照)の中で参照されるときに,もしMが静的イベ
ントであるならば,Eは型を表し,もしMがインスタンスイベントであるならば,Eはインスタンスを表
さなければならない。
静的メンバとインスタンスメンバとの間の違いは,17.2.5で更に詳しく規定する。
17.7.4 仮想,封印,上書き及び抽象のアクセス子
イベント宣言virtualは,そのイベントアクセス子が仮想であることを指定する。修飾子virtualは,
イベントの両方のアクセス子に適用される。
イベント宣言abstractは,そのイベントアクセス子が仮想アクセス子であり,アクセス子は実際には
349
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
実装されないことを指定する。アクセス子の独自の実装を提供するためには,非抽象派生クラスがイベン
トを上書きすることが必要となる。抽象イベント宣言のアクセス子は,実際に実装されないので,≪アク
セス子本体≫を構成するのはセミコロンだけとする。
修飾子abstract及びoverrideの両方を含むイベント宣言は,そのイベントが抽象であり,基底イ
ベントを上書きすることを指定する。この場合のイベントアクセス子も抽象アクセス子とする。
抽象イベント宣言は,抽象クラス(17.1.1.1参照)の中でだけ許可される。
継承された仮想イベントアクセス子は,修飾子overrideを指定するイベント宣言を含めることによっ
て,派生クラス内で上書きできる。これは,上書きするイベント宣言と呼ばれる。上書きするイベント宣
言では,新規イベントは宣言されない。代わりに,既存の仮想イベントアクセス子に対する実装を特化す
るだけである。
上書きするイベント宣言では,上書きされるイベントと全く同じアクセス可能性修飾子,型及び名前を
指定する必要がある。
上書きするイベント宣言は,修飾子sealedを含んでよい。この修飾子を使用すると,派生クラスでイ
ベントをこれ以上上書きしないように封印できる。封印イベントアクセス子も封印される。
上書きするイベント宣言に修飾子newを含めるとコンパイル時エラーになる。
構文上の宣言及び呼出しにおける違いを除けば,仮想,封印,上書き及び抽象の各アクセス子は,仮想,
封印,上書き及び抽象の各メソッドと全く同じように動作する。具体的には,17.5.3,17.5.4,17.5.5及び
17.5.6で規定した規則が,そのアクセス子があたかも対応する形式のメソッドであるかのように適用され
る。各アクセス子は,イベント型の単一値仮引数,返却値の型void及び包含するイベントと同じ修飾子
をもつメソッドに対応する。
17.8 添字子
添字子は,配列と同じ方法でオブジェクトを添字指定することを可能にするメンバとする。添字子は≪
添字子宣言≫を使って宣言される。
≪添字子宣言≫:
≪属性群≫opt ≪添字子修飾子群≫opt ≪添字子宣言子≫ { ≪アクセス子宣言群
≫ }
≪添字子修飾子群≫:
≪添字子修飾子≫
≪添字子修飾子群≫ ≪添字子修飾子≫
≪添字子修飾子≫:
new
public
protected
internal
private
virtual
sealed
override
abstract
extern
350
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪添字子宣言子≫:
≪型≫ this [ ≪仮引数並び≫ ]
≪型≫ ≪インタフェース型≫ . this [ ≪仮引数並び≫ ]
≪添字子宣言≫は,≪属性≫(箇条24参照)の集合,及び四つのアクセス修飾子(17.2.3参照),修飾
子new(17.2.2参照),virtual(17.5.3参照),override(17.5.4参照),sealed(17.5.5参照),abstract
(17.5.6参照)及びextern(17.5.7参照)の有効な組合せを含むことができる。
添字子宣言は,修飾子の有効な組合せに関しては添字子宣言で修飾子staticが許されないということ
を除き,メソッド宣言(17.5参照)と同じ規則が適用される。
修飾子virtual,override及びabstractは,次の場合を除けば相互排他となる。すなわち,修飾
子abstract及びoverrideだけは,抽象添字子が仮想添字子を上書きするので,一緒に用いてもよい。
添字子宣言の≪型≫は,その宣言で導入される添字子の要素型を指定する。添字子が明示的なインタフ
ェースメンバの実装でない場合,≪型≫の後にはキーワードthisを指定する。明示的なインタフェース
メンバ実装では,≪型≫の後に≪インタフェース型≫,“.”及びキーワードthisを指定する。他のメン
バとは異なり,添字子には利用者定義の名前がない。
≪仮引数並び≫は,添字子の仮引数を指定する。添字子の仮引数並びは,少なくとも一つの仮引数が指
定されなければならないこと,及び仮引数修飾子ref及びoutが許されないことを除けば,メソッド
(17.5.1参照)の仮引数並びと同じである。
添字子の≪型≫及び≪仮引数並び≫の中で参照される各型及び添字子の≪型≫は,少なくとも添字子自
身と同じアクセス可能性をもたなければならない(10.5.4参照)。
≪アクセス子宣言群≫(17.6.2参照)は,添字子のアクセス子を宣言するが,字句“{”及び“}”で囲
まれていなければならない。アクセス子は,添字子要素の読込み及び書出しに関連付ける実行可能な文を
指定する。
添字子要素にアクセスするための構文は,配列要素にアクセスするための構文と同じであるが,添字子
要素は,変数には分類されない。したがって,実引数ref又はoutとして,添字子要素を渡すことはで
きない。
添字子の≪仮引数並び≫は,その添字子の呼出し情報(10.6参照)を定義する。具体的には,添字子の
呼出し情報は,仮引数の数及びそれらの型で構成される。仮引数の要素型及び名前は,添字子の呼出し情
報に含まれない。
添字子の呼出し情報は,同一クラスで宣言されている他のすべての添字子の呼出し情報とは異ならなけ
ればならない。
添字子及び特性は,概念上非常に似ているが,次の7点で異なる。
− 特性は名前で識別されるが,添字子は呼出し情報で識別される。
− 特性は≪単純名≫(14.5.2参照)又は≪メンバアクセス≫(14.5.4参照)でアクセスされるが,添字子
要素は≪要素アクセス≫(14.5.6参照)でアクセスされる。
− 特性はstaticメンバにすることができるが,添字子は常にインスタンスメンバとする。
− 特性のアクセス子getは仮引数をもたないメソッドに対応するが,添字子のアクセス子getは添字
子と同じ仮引数並びをもつメソッドに対応する。
− 特性のアクセス子setはvalueという名前の単一の仮引数をもつメソッドに対応するが,添字子の
アクセス子setは添字子と同じ仮引数並びをもち,更にvalueという名前の追加の仮引数をもつメ
ソッドに対応する。
351
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 添字子アクセス子で,添字子仮引数と同じ名前の局所変数又は局所定数を宣言するとコンパイル時エ
ラーになる。
− 上書きする特性宣言の中で,継承される特性は,Pが特性名であれば,構文base.Pを用いてアクセ
スされる。上書きする添字子宣言の中では,継承される添字子は,Eが式のコンマ区切り並びであれ
ば,構文base[E]を用いてアクセスされる。
これらの違いを除けば,17.6.2及び17.6.3で定義したすべての規則が,特性のアクセス子に対してと同
様に添字子のアクセス子に適用される。
添字子の宣言に修飾子externが含まれる場合,その添字子は外部添字子であると呼ばれる。外部添字
子の宣言は実際に実装されないので,各≪アクセス子宣言群≫を構成するのはセミコロンだけとする。
例1 次の例は,ビット配列のそれぞれのビットをアクセスするために,添字子を実装するクラス
BitArrayを宣言している。
using System;
class BitArray
{
int[] bits;
int length;
public BitArray(int length) {
if (length < 0) throw new ArgumentException();
bits = new int[((length - 1) >> 5) + 1];
this.length = length;
}
public int Length {
get { return length; }
}
public bool this[int index] {
get {
if (index < 0 || index >= length) {
throw new IndexOutOfRangeException();
}
return (bits[index >> 5] & 1 << index) != 0;
}
set {
if (index < 0 || index >= length) {
throw new IndexOutOfRangeException();
}
if (value) {
bits[index >> 5] |= 1 << index;
}
else {
bits[index >> 5] &= ~(1 << index);
352
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
}
}
}
クラスBitArrayのインスタンスは,実質的には相当するbool[]よりも少ないメモリを消
費する(後者の各値は1バイトを占めるが,前者の各値は1ビットしか占めないからである)
が,bool[]の場合と同じ演算ができる。
次のクラスCountPrimesは,2と与えられた最大値との間の素数を計算するために,
BitArray及び古典的な“エラトステネスのふるい”アルゴリズムを使用する。
class CountPrimes
{
static int Count(int max) {
BitArray flags = new BitArray(max + 1);
int count = 0;
for (int i = 2; i <= max; i++) {
if (!flags[i]) {
for (int j = i * 2; j <= max; j += i) flags[j]
= true;
count++;
}
}
return count;
}
static void Main(string[] args)
int max = int.Parse(args[0]);
int count = Count(max);
Console.WriteLine("Found {0} primes between 1 and {1}", count,
max);
}
}
BitArrayのアクセスする要素の構文はbool[]の場合と全く同じであることに注意する。
例2 次の例は,二つの仮引数をもつ添字子をもつ26×10の格子クラスを示す。最初の仮引数はA
〜Zの値域の大文字又は小文字,2番目の仮引数は0〜9の値域の整数である必要がある。
using System;
class Grid
{
const int NumRows = 26;
const int NumCols = 10;
int[,] cells = new int[NumRows, NumCols];
353
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public int this[char c, int colm]
{
get {
c = Char.ToUpper(c);
if (c < 'A' || c > 'Z') {
throw new ArgumentException();
}
if (colm < 0 || colm >= NumCols) {
throw new IndexOutOfRangeException();
}
return cells[c - 'A', colm];
}
set {
c = Char.ToUpper(c);
if (c < 'A' || c > 'Z') {
throw new ArgumentException();
}
if (colm < 0 || colm >= NumCols) {
throw new IndexOutOfRangeException();
}
cells[c - 'A', colm] = value;
}
}
}
17.8.1 添字子の多重定義
添字子の多重定義解決規則は,14.4.2で規定する。
17.9 演算子
演算子は,そのクラスのインスタンスに適用できる式演算子の意味を定義するメンバとする。演算子は
≪演算子宣言≫を使って宣言される。
≪演算子宣言≫:
≪属性群≫opt ≪演算子修飾子群≫ ≪演算子宣言子≫ ≪演算子本体≫
≪演算子修飾子群≫:
≪演算子修飾子≫
≪演算子修飾子群≫ ≪演算子修飾子≫
≪演算子修飾子≫:
public
static
extern
≪演算子宣言子≫:
≪単項演算子宣言子≫
354
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪2項演算子宣言子≫
≪変換演算子宣言子≫
≪単項演算子宣言子≫:
≪型≫ operator ≪多重定義可能単項演算子≫ ( ≪型≫ ≪識別子≫ )
≪多重定義可能単項演算子≫: 次のいずれか
+
-
!
~
++
--
true
fale
≪2項演算子宣言子≫:
≪型≫ operator ≪多重定義可能2項演算子≫ ( ≪型≫ ≪識別子≫ , ≪
型≫ ≪識別子≫ )
≪多重定義可能2項演算子≫: 次のいずれか
+ - * / % & | ^ << ≪右シフト≫ == != > < >= <=
≪変換演算子宣言子≫:
implicit operator ≪型≫ ( ≪型≫ ≪識別子≫ )
explicit operator ≪型≫ ( ≪型≫ ≪識別子≫ )
≪演算子本体≫:
≪ブロック≫
;
多重定義可能な演算子は,単項演算子(17.9.1参照),2項演算子(17.9.2参照)及び変換演算子(17.9.3
参照)の3種類とする。
演算子宣言が修飾子externを含むとき,その演算子は外部演算子であると呼ばれる。外部演算子は実
際の実装を提供しないので,その≪演算子本体≫は一つのセミコロンで構成される。その他のすべての演
算子の場合,≪演算子本体≫を構成するのは,演算子呼出し時に実行する文を指定する≪ブロック≫とす
る。演算子の≪ブロック≫は,17.5.8で規定した値を返すメソッドのための規則に適合しなければならな
い。
すべての演算子の宣言には次の規則が適用される。
− 演算子宣言は,修飾子publicとstaticとの両方を含まなければならない。
− 演算子の仮引数は,値仮引数でなければならない。演算子宣言に仮引数ref又はoutを指定すると,
コンパイル時エラーになる。
− 演算子の呼出し情報(17.9.1,17.9.2及び17.9.3参照)は,同じクラスの中で宣言された他のすべての
演算子の呼出し情報と異ならなければならない。
− 演算子宣言の中で参照しているすべての型は,少なくともその演算子自身(10.5.4参照)と同じアク
セス可能性をもたなければならない。
− 一つの演算子宣言内で同じ修飾子を複数回使用すると,エラーになる。
各演算子の種類に応じて,17.9.1〜17.9.3で規定するように,追加の制限事項がある。
基底クラスで宣言された演算子は,他のメンバと同様に派生クラスによって継承される。演算子の宣言
では,その演算子が宣言されるクラス又は構造体を演算子の呼出し情報に含める必要があるので,基底ク
ラスで宣言された演算子を派生クラスで宣言された演算子で隠ぺいすることはできない。したがって,演
算子の宣言では修飾子newが必要になったり許可されたりすることはない。
355
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
単項及び2項演算子の更なる情報は,14.2で規定する。
変換演算子の更なる情報は,13.4で規定する。
17.9.1 単項演算子
単項演算子宣言には,次の3規則を適用する。ここで,Tは,その演算子宣言を含むクラス又は構造体
型を表す。
− 単項演算子+,-,!又は~は,型T又はT?の単一仮引数を取らなければならないが,任意の型を返す
ことができる。
− 単項演算子++又は--は,型T又はT?の単一仮引数を取り,返却値はそれと同じ型でなければならな
い。
− 単項演算子true又はfalseは,型T又はT?の単一仮引数を取り,返却値は型boolでなければな
らない。
単項演算子の呼出し情報は,演算子字句(+,-,!,~,++,--,true又はfalse)及び単一仮引数
の型で構成される。返却値の型は,単項演算子の呼出し情報に含まれない。また,仮引数の名前も含まれ
ない。
単項演算子true及びfalseは,これらの宣言が必ず対で行われなければならない。クラスがこれらの
演算子の一方だけを宣言すると,コンパイル時エラーになる。演算子true及びfalseは14.17で更に規
定する。
例 整数ベクトルクラスの演算子++の実装とその後の使用について,次の例で示す。
public class IntVector
{
public int Length { … }
// read-only property
public int this[int index] { … }
// read-write indexer
public IntVector(int vectorLength) { … }
public static IntVector operator++(IntVector iv) {
IntVector temp = new IntVector(iv.Length);
for (int i = 0; i < iv.Length; ++i)
temp[i] = iv[i] + 1;
return temp;
}
}
class Test
{
static void Main() {
IntVector iv1 = new IntVector(4); // vector of 4x0
IntVector iv2;
iv2 = iv1++; // iv2 contains 4x0, iv1 contains 4x1
iv2 = ++iv1; // iv2 contains 4x2, iv1 contains 4x2
}
}
356
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
演算子メソッドが,後置増加演算子及び後置減少演算子(14.5.9参照),並びに,前置増加演算
子及び前置減少演算子(14.6.5参照)と全く同様に,どのように演算対象に1を加算して得られ
た値を返しているかに注意する。C++の場合とは異なり,このメソッドは演算対象の値を直接変
更する必要はなく,実際に直接変更してはならない。
17.9.2 2項演算子
2項演算子宣言には,次の二つの規則を適用する。ここで,Tは,その演算子宣言を含むクラス又は構
造体型を表す。
− シフト演算子を除く2項演算子は,二つの仮引数を取る必要があり,そのうちの少なくとも一つは型
T又はT?でなければならず,任意の型を返すことができる。
− 2項演算子<<及び2項演算子>>(14.8参照)は,二つの仮引数を取る必要があり,1番目は型T又は
T?を及び2番目は型int又はint?をもたねばならず,任意の型を返すことができる。
2項演算子の呼出し情報は,演算子字句(+,-,*,/,%,&,|,^,<<,≪右シフト≫,==,!=,>,
<,>=又は<=)及び二つの仮引数の型で構成される。返却値の型及び仮引数の名前は,2項演算子の呼出し
情報に含まれない。
幾つかの2項演算子は対で宣言する必要がある。対の一方の演算子を宣言する場合は,対の他方の演算
子に合致する宣言が存在する必要がある。二つの演算子の宣言が合致するためには,返却値の型及び各仮
引数の型が同じでなければならない。対で宣言する必要があるのは,次の2項演算子とする。
− operator ==及びoperator !=
− operator >及びoperator <
− operator >=及びoperator <=
17.9.3 変換演算子
変換演算子宣言は,実引数のあらかじめ定義された明示及び暗黙の変換である利用者定義変換演算子
(13.4参照)を導入する。
キーワードimplicitを含む変換演算子宣言は,利用者定義の暗黙の変換演算子を導入する。暗黙の変
換は,関数メンバ呼出し,キャスト式,代入など,様々な状況で実行される。これは,13.1で更に規定す
る。
キーワードexplicitを含む変換演算子宣言は,利用者定義の明示的な変換演算子を導入する。明示的
な変換はキャスト式の中で使うことができ,13.2で更に規定する。
変換演算子は,変換演算子の仮引数型で表される元の型から,変換演算子の返却値の型で表される変換
先の型に変換する。次の4条件すべてが満たされる場合に限り,クラス及び構造体は,元の型Sから目的
の型Tへの変換を宣言できる。ここで,S0及びT0はS及びTの後に?修飾子が存在する場合にそれを削
除した結果の型とする。
− S0及びT0とは異なる型である。
− S0及びT0は,その演算子宣言が行われているクラス又は構造体型である。
− S0及びT0のいずれも≪インタフェース型≫でない。
− ユーザ定義演算子を除き,SからT又はTからSへの変換が存在しない。
2番目の規則から,変換演算子は,演算子の宣言が行われるクラス又は構造体型を変換先又は変換元と
する必要があることが分かる。
例 あるクラス又は構造体型Cは,Cからintへの変換及びintからCへの変換を定義できるが,
intからboolへの変換は定義できない。
357
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
あらかじめ定義された変換を再定義することはできない。したがって,明示及び暗黙の変換が既にオブ
ジェクトと他のすべての型の間に存在するので,変換演算子はobjectから又はobjectに変換できない。
同様に,元の型と目的の型のいずれも,他方の基底型になることはできない。これは,変換が既に存在す
るからである。
利用者定義の変換で,≪インタフェース型≫を変換元又は変換先にすることはできない。より具体的に
は,この制限によって,≪インタフェース型≫に変換するときに,利用者定義の変換が実行されないこと,
及び変換中のオブジェクトが指定された≪インタフェース型≫を実際に実装する場合にだけ≪インタフェ
ース型≫への変換が成功することが保証される。
変換演算子の呼出し情報は,元の型及び目的の型で構成される(変換演算子だけが,呼出し情報に返却
値の型が関係するメンバとなる)。変換演算子のimplicit又はexplicitの分類は,演算子の呼出し情
報の一部ではない。したがって,クラス又は構造体は同じ元の型及び目的の型に変換演算子implicit及
びexplicitの両方を宣言できない。
注記 通常,利用者定義の暗黙の変換は,例外を決して送出せず,情報を失わないように設計する必
要がある。利用者定義の変換が例外を発生したり(変換元の実引数が値域外であるなど),情報
を失ったり(上位ビットを破棄するなど)する場合は,その変換は明示的な変換として定義す
る必要がある。
例
using System;
public struct Digit
{
byte value;
public Digit(int value) {
if (value < 0 || value > 9) throw new ArgumentException();
this.value = value;
}
public static implicit operator byte(Digit d) {
return d.value;
}
public static explicit operator Digit(int n) {
return new Digit(n);
}
}
このコードでは,Digitからintへの変換演算子は,決して例外を送出したり,情報を失っ
たりしないので暗黙とする。しかし,intからDigitへの変換は,Digitがintの可能な値の
一部しか表現できないので明示とする。
17.10 インスタンス構築子
インスタンス構築子は,クラスのインスタンスを初期化するために必要な動作を実装するメンバとする。
インスタンス構築子は≪構築子宣言≫を使って宣言される。
≪構築子宣言≫:
≪属性群≫opt ≪構築子修飾子群≫opt ≪構築子宣言子≫ ≪構築子本体≫
358
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪構築子修飾子群≫:
≪構築子修飾子≫
≪構築子修飾子群≫ ≪構築子修飾子≫
≪構築子修飾子≫:
public
protected
internal
private
extern
≪構築子宣言子≫:
≪識別子≫ ( ≪仮引数並び≫opt ) ≪構築子初期化子≫opt
≪構築子初期化子≫:
: base ( ≪実引数並び≫opt )
: this ( ≪実引数並び≫opt )
≪構築子本体≫:
≪ブロック≫
;
≪構築子宣言≫は,≪属性≫(箇条24参照)の集合,四つのアクセス修飾子(17.2.3参照)及び修飾子
extern(17.5.7参照)の有効な組合せを含むことができる。構築子宣言には,同じ修飾子を複数回含める
ことはできない。
≪構築子宣言子≫の≪識別子≫は,インスタンス構築子が宣言されるクラスの名前を指定する必要があ
る。他の名前を指定した場合は,コンパイル時エラーになる。
省略可能なインスタンス構築子の≪仮引数並び≫は,メソッド(17.5参照)の≪仮引数並び≫と同じ規
則が適用される。その仮引数並びは,インスタンス構築子の呼出し情報(10.6参照)を定義し,多重定義
解決(14.4.2参照)が呼出しの中の特別なインスタンス構築子を選択する処理を支配する。
インスタンス構築子の≪仮引数並び≫の中で参照されている各型は,少なくとも構築子自身(10.5.4参
照)と同じアクセス可能性をもたなければならない。
省略可能な≪構築子初期化子≫は,このインスタンス構築子の≪構築子本体≫で指定された文を実行す
る前に呼び出す別のインスタンス構築子を指定する。これは17.10.1で更に規定する。
構築子の宣言に修飾子externが含まれる場合,その構築子を外部構築子という。
外部構築子宣言は,実際には実装を提供されないので,≪構築子本体≫を構成するのはセミコロンだけ
とする。その他すべての構築子の場合,≪構築子本体≫を構成するのは,クラスの新規インスタンスを初
期化する文を規定する≪ブロック≫となる。これは,返却値の型void(17.5.8参照)をもつインスタンス
メソッドの≪ブロック≫に全く対応する。
インスタンス構築子は継承されない。このため,クラスは,そのクラスで実際に宣言されたもの以外の
インスタンス構築子を保有しない。もしクラスがインスタンス構築子宣言を含まないならば,省略時のイ
ンスタンス構築子が自動的に提供される(17.10.4参照)。
インスタンス構築子は≪オブジェクト生成式≫(14.5.10.1参照)及び≪構築子初期化子≫によって呼び
出される。
359
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
17.10.1
構築子初期化子
すべてのインスタンス構築子(クラスobjectのものを除く)は,≪構築子本体≫の直前にある別のイ
ンスタンス構築子の呼出しを暗黙に含む。暗黙に呼び出す構築子は,≪構築子初期化子≫によって決定さ
れる。
− base (≪実引数並び≫opt)形式のインスタンス構築子初期化子は,直接基底クラスからインスタンス
構築子を呼び出す。その構築子は≪実引数並び≫及び14.4.2の多重定義解決規則を用いて選択される。
インスタンス構築子の候補の集合は,直接基底クラスの中で宣言されたすべてのアクセス可能なイン
スタンス構築子又はインスタンス構築子が直接基底クラスの中で宣言されていない場合の省略時構築
子(17.10.4参照)で構成される。この集合が空である場合,又は,一つの最適なインスタンス構築子
を識別できなかった場合は,コンパイル時エラーになる。
− this (≪実引数並び≫opt)形式のインスタンス構築子初期化子は,クラス自身からインスタンス構築
子を呼び出す。その構築子は≪実引数並び≫及び14.4.2の多重定義解決規則を用いて選択される。イ
ンスタンス構築子の候補の集合は,そのクラス自体で宣言された,アクセス可能なすべてのインスタ
ンス構築子とする。この集合が空である場合,又は一つの最適なインスタンス構築子を識別できなか
った場合は,コンパイル時エラーになる。インスタンス構築子の宣言に,その構築子自体を呼び出す
構築子初期化子が含まれる場合は,コンパイル時エラーになる。
インスタンス構築子が構築子初期化子をもたないならば,base()形式の構築子初期化子が暗黙に提供
される。
注記 したがって,次の形式のインスタンス構築子宣言を考える。
C(…) {…}
これは,次の形式と全く同等である。
C(…): base() {…}
インスタンス構築子の宣言の≪仮引数並び≫で与えられる仮引数の有効範囲には,その宣言の構築子初
期化子が含まれる。したがって,構築子の仮引数にアクセスするために構築子初期化子を使うことができ
る。
例
class A
{
public A(int x, int y) {}
}
class B: A
{
public B(int x, int y): base(x + y, x - y) {}
}
例えば,インスタンス構築子初期化子は,生成中のインスタンスにはアクセスできない。このため,構
築子初期化子の実引数式でthisを参照するとコンパイル時エラーになる。同様に,実引数式が≪単純名
≫を使ってインスタンスメンバを参照するとコンパイル時エラーになる。
17.10.2
インスタンス変数初期化子
インスタンス構築子に構築子初期化子が指定されていない場合,又はbase(...)形式の構築子初期化
子が指定されている場合は,その構築子はそのクラスで宣言されたインスタンスフィールドの≪変数初期
360
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
化子≫で指定された初期化を暗黙に実行する。これは,構築子への入口直後,及び直接基底クラスの構築
子の暗黙の呼出しの直前に実行される一連の代入に対応する。変数初期化子は,クラス宣言に出てくる順
番どおりに実行される(17.4.5参照)。
17.10.3
構築子の実行
変数初期化子は代入文に変換され,この代入文は基底クラスのインスタンス構築子の呼出し前に実行さ
れる。この実行順序は,インスタンスにアクセスする任意の文が実行される前に,その変数初期化子によ
ってすべてのインスタンスフィールドの初期化を保証する。
例
using System;
class A
{
public A() {
PrintFields();
}
public virtual void PrintFields() {}
}
class B: A
{
int x = 1;
int y;
public B() {
y = -1;
}
public override void PrintFields() {
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}
Bのインスタンスを生成するためにnew B()が使用されたとき,次の出力が生成される。
x = 1, y = 0
基底クラスのインスタンス構築子が呼び出される前に変数初期化子が実行されるので,xの値
は1である。しかし,yへの代入はその基底クラス構築子から戻るまで実行されないので,yの
値は0(intの省略時の値)である。
インスタンス変数初期化子及び構築子初期化子は,≪構築子本体≫の前に自動挿入される文と
して考えるとよい。次に例を示す。
using System;
using System.Collections;
class A
{
int x = 1, y = -1, count;
public A() {
361
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
count = 0;
}
public A(int n) {
count = n;
}
}
class B: A
{
double sqrt2 = Math.Sqrt(2.0);
ArrayList items = new ArrayList(100);
int max;
public B(): this(100) {
items.Add("default");
}
public B(int n): base(n ‒ 1) {
max = n;
}
}
この例は幾つかの変数初期化子に加えて,両方の形式(base及びthis)の構築子初期化子も
含む。この例は,次に示すコードに対応している。注釈は自動挿入される文を示している。自動
挿入される構築子の呼出しに使用される構文は有効ではなく,この機構を示すためだけのもので
ある。
using System.Collections;
class A
{
int x, y, count;
public A() {
x = 1;
// Variable initializer
y = -1;
// Variable initializer
object();
// Invoke object() constructor
count = 0;
}
public A(int n) {
x = 1;
// Variable initializer
y = -1;
// Variable initializer
object();
// Invoke object() constructor
count = n;
}
}
class B: A
362
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
double sqrt2;
ArrayList items;
int max;
public B(): this(100) {
B(100);
// Invoke B(int) constructor
items.Add("default");
}
public B(int n): base(n ‒ 1) {
sqrt2 = Math.Sqrt(2.0);
// Variable initializer
items = new ArrayList(100); // Variable initializer
A(n ‒ 1);
// Invoke A(int) constructor
max = n;
}
}
17.10.4
省略時構築子
クラスにインスタンス構築子の宣言が全く含まれない場合は,省略時のインスタンス構築子が自動的に
提供される。省略時の構築子は,直接基底クラスの引数なしの構築子を呼び出すだけとする。アクセス可
能な引数なしのインスタンス構築子が直接基底クラス内にない場合は,コンパイル時エラーになる。クラ
スが抽象クラスの場合,省略時の構築子の宣言されたアクセス可能性は限定公開とする。それ以外の場合,
省略時の構築子の宣言されたアクセス可能性は公開とする。
注記 したがって,その省略時構築子は,常に
protected C(): base() {}
又は
public C(): base() {}
の形式になる。ここでCはクラスの名前とする。
例
class Message
{
object sender;
string text;
}
このコードでは,クラスにインスタンス構築子の宣言が含まれないので,省略時の構築子が提
供される。したがって,次の例と全く同等である。
class Message
{
object sender;
string text;
public Message(): base() {}
}
363
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
17.10.5
非公開構築子
クラスが非公開インスタンス構築子だけを宣言するとき,(そのクラスの中で入れ子になっていない)他
のクラスをそのクラスから派生することはできないし,他のクラスでそのクラスのインスタンスを生成す
ることもできない。
例 非公開インスタンス構築子は,指示されたインスタンス集合に対して,そのクラスの具現化を制
限するために使用できる。次に例を示す。
public class Color
{
private byte val;
private Color(byte val) { this.val = val; }
public static readonly Color Red = new Color(0);
public static readonly Color Green = new Color(1);
public static readonly Color Blue = new Color(2);
}
Colorの構築子は非公開なので,クラスColorのインスタンスは,静的フィールドに保存さ
れ,その静的特性によって返される三つしかない。
省略時構築子の自動生成を抑止するには,少なくとも一つのインスタンス構築子を宣言しなければなら
ない。
17.10.6
省略可能なインスタンス構築子の仮引数
注記 構築子初期化子のthis(…)形式は,一般に省略可能なインスタンス構築子の仮引数を実装する
ために,多重定義と併せて使用される。次に例を示す。
class Text
{
public Text(): this(0, 0, null) {}
public Text(int x, int y): this(x, y, null) {}
public Text(int x, int y, string s) {
// Actual constructor implementation
}
}
先頭から二つのインスタンス構築子は,指定されなかった実引数に対する省略時の値を提供
するだけである。いずれも構築子初期化子this(…)を用いて,3番目のインスタンス構築子を
呼び出す。この構築子は新規インスタンスの初期化作業を実際に行う。その効果として,次に
示すように仮引数を省略して構築子を呼び出すことができる。
Text t1 = new Text();
// Same as Text(0, 0, null)
Text t2 = new Text(5, 10);
// Same as Text(5, 10, null)
Text t3 = new Text(5, 20, "Hello");
17.11
静的構築子
静的構築子は,クラスを初期化するために必要な動作を実装するメンバとする。静的構築子は≪静的構
築子宣言≫を使って宣言される。
≪静的構築子宣言≫:
364
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪属性群≫opt ≪静的構築子修飾子群≫ ≪識別子≫ ( ) ≪静的構築子本体≫
≪静的構築子修飾子群≫:
externopt static
static externopt
≪静的構築子本体≫:
≪ブロック≫
;
≪静的構築子宣言≫は,≪属性≫(箇条24参照)の集合及びextern修飾子(17.5.7参照)を含んでも
よい。
≪静的構築子宣言≫の≪識別子≫は,静的構築子が宣言されるクラスの名前を指定する必要がある。他
の名前を指定した場合は,コンパイル時エラーになる。
静的構築子宣言に修飾子externが含まれる場合,その静的構築子を外部静的構築子という。外部静的
構築子宣言では実際の実装は提供されないので,≪静的構築子本体≫を構成するのはセミコロンだけとな
る。その他のすべての静的構築子宣言の場合,≪静的構築子本体≫を構成するのは,クラスを初期化する
ために実行する文を指定する≪ブロック≫とする。これは,返却値の型void(17.5.8参照)をもつ静的メ
ソッドの≪メソッド本体≫と全く同等である。
静的構築子は継承されず,直接呼び出すこともできない。
一つの非総称クラスに対する静的構築子は,指定のアプリケーション領域で高々1回だけ実行される。
一つの総称クラス宣言に対する静的構築子は,そのクラス宣言から構築される各閉構築型に対して高々1
回だけ実行される(25.1.5参照)。静的構築子の実行は,次のいずれかの事象がアプリケーション領域で最
初に発生したときになされる。
− クラスのインスタンスが生成されたとき
− クラスのいずれかの静的メンバが参照されたとき
もしクラスが実行を開始するメソッドMain(10.1参照)を含むならば,そのクラスの静的構築子はそ
のメソッドMainが呼び出される前に実行される。初期化子をもつ静的フィールドがクラスに含まれてい
る場合は,静的構築子を実行する直前に,記述された順序どおりに初期化子が実行される(17.4.5参照)。
例 次に例を示す。
using System;
class Test
{
static void Main() {
A.F();
B.F();
}
}
class A
{
static A() {
Console.WriteLine("Init A");
}
365
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public static void F() {
Console.WriteLine("A.F");
}
}
class B
{
static B() {
Console.WriteLine("Init B");
}
public static void F() {
Console.WriteLine("B.F");
}
}
この出力は,次のようになる。
Init A
A.F
Init B
B.F
この理由は,Aの静的構築子の実行はA.Fに対する呼出しによって引き起こされ,Bの静的構
築子の実行はB.Fに対する呼出しによって引き起こされることによる。
変数初期化子をもつ静的フィールドが省略時の値の状態であることを確認する循環依存関係を構築でき
る。
例
using System;
class A
{
public static int X;
static A() { X = B.Y + 1;}
}
class B
{
public static int Y = A.X + 1;
static B() {}
static void Main() {
Console.WriteLine("X = {0}, Y = {1}", A.X, B.Y);
}
}
この例では,次のように出力される。
X = 1, Y = 2
メソッドMainを実行するために,システムはクラスBの静的構築子の前に最初にB.Yに対し
366
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
てその初期化子を実行する。Yの初期化子でA.Xの値が参照されるので,Aの静的構築子を実行
する。次に,Aの静的構築子はXの値を計算するためにYの省略時の値であるゼロを取り出す。
したがって,A.Xは1に初期化される。次にAの静的フィールド初期化子及び静的構築子の実行
の処理を計算し,Yの初期値の計算を返し,その結果は2になる。
17.12
終了化子
注記 この規格の以前の版では,現在“終了化子”としているものを“解体子”と呼んでいた。特に
C++を知っているプログラマにとって,用語“解体子”は混乱をもたらし,しばしば誤った期
待を招くことが明らかになった。C++の解体子は決定した方法で呼びされるのに対して,C#の
終了化子はそうではない。C#で決定した振る舞いを得るためには,Disposeを使わねばなら
ない。
終了化子は,クラスのインスタンスを終了化するために必要な動作を実装するメンバとする。終了化子
は≪終了化子宣言≫を使って宣言される。
≪終了化子宣言≫:
≪属性群≫opt externopt ~ ≪識別子≫ ( ) ≪終了化子本体≫
≪終了化子本体≫:
≪ブロック≫
;
≪終了化子宣言≫は,≪属性≫(箇条24参照)の集合を含むことができる。
≪終了化子宣言≫の≪識別子≫は,終了化子が宣言されるクラスの名前を指定する必要がある。他の名
前を指定した場合は,コンパイル時エラーになる。
終了化子宣言に修飾子externが含まれる場合,その終了化子を外部終了化子であるという。外部終了
化子宣言は実際には実装が提供されないので,≪終了化子本体≫を構成するのはセミコロンだけとなる。
その他のすべての終了化子の場合,≪終了化子本体≫を構成するのは,クラスのインスタンスを終了化す
るために実行する文を指定する≪ブロック≫とする。≪終了化子本体≫は返却値の型void(17.5.8参照)
をもつインスタンスメソッドの≪メソッド本体≫と全く同等である。
終了化子は継承されない。このため,クラスはそのクラスで実際に宣言されたもの以外の終了化子を保
有しない。
注記 終了化子は仮引数をもつ必要がないので,多重定義できない。したがって,クラスは高々一つ
の終了化子しかもつことができない。
終了化子は自動的に呼び出され,明示的に呼び出すことはできない。そのインスタンスをどのコードも
使用できなくなると,そのインスタンスは解体の資格を得る。インスタンスの終了化子の実行は,インス
タンスが解体の資格を得た後は,いつでも発生できる。インスタンスが終了化されたとき,インスタンス
の継承連鎖の中で,終了化子が最派生のものから基底のものへと順に呼び出される。
例
using System;
class A
{
~A() {
Console.WriteLine("A's finalizer ");
}
367
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
class B: A
{
~B() {
Console.WriteLine("B's finalizer ");
}
}
class Test
{
static void Main() {
B b = new B();
b = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
この例は,次のように出力する。
Bʼs finalizer
Aʼs finalizer
これは,継承連鎖内の終了化子が最派生から基底へと順に呼び出されることによる。
注記 補足されない例外が送出された場合,基底クラスの終了化子が呼び出される(23.3参照)
かどうかは規定されない。
C#の実装では,System.Objectの仮想メソッドFinalizeを上書きすることで終了化子を実装して
もよい。しかし,どのような場合でも,C#プログラムで,このメソッドを上書きしてはならない,また,
それ(又はその上書きしたもの)を直接呼び出してはならない。
例
class A
{
protected override void Finalize() {}
// error
public void F() {
this.Finalize();
// error
}
}
例えば,このプログラムにはエラーが2か所ある。
コンパイラは,あたかもSystem.Object.Finalize及びその上書きが全く存在していないかのよう
に動作する。
例
class A
{
void Finalize() {}
// permitted
368
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
したがって,このプログラムは有効であり,ここで示されたメソッドは新しい修飾子を要求す
ることなしにSystem.ObjectのメソッドFinalizeを隠ぺいする。
例外が終了化子から送出されるときの挙動は,23.3に規定する。
18 構造体
構造体はクラスに似ていて,データメンバ及び関数メンバを格納できるデータ構造を表現する。しかし,
クラスとは異なり,構造体は値型であり,ヒープ割当てを必要としない。構造体型の変数は構造体のデー
タを直接格納する。クラス型の変数はデータへの参照を格納する。後者はオブジェクトと呼ばれる。
注記 構造体は値文脈をもつ小さなデータ構造を扱う際に特に有用である。構造体に適した例として
は,複雑な数値,座標システム内の点,及び,辞書内のキー値の対がある。これらのデータ構
造の特徴は,数個のデータメンバをもち,継承及び参照識別性を使用する必要がなく,代入が
参照ではなく値をコピーするような値文脈を用いて実装できることである。
11.1.3で規定したように,C#の規定するint,double,boolなどの単純型は,実際にはすべて構造体
型になる。これらの既定義の型が構造体であるのと同様に,C#言語では新しい基本的な型を実装するため
に構造体及び演算子の多重定義を使用できる。
18.1 構造体宣言
≪構造体宣言≫は新しい構造体を宣言する≪型宣言≫(16.6参照)とする。
≪構造体宣言≫:
≪属性群≫opt ≪構造体修飾子群≫opt partialopt struct ≪識別子≫
≪型仮引数並び≫opt ≪構造体インタフェース群≫opt
≪型仮引数制約群節群≫opt ≪構造体本体≫ ;opt
≪構造体宣言≫は,省略可能な≪属性群≫(箇条24参照)の集合,省略可能な≪構造体修飾子群≫(18.1.1
参照)の集合,省略可能なpartial修飾子(17.1.4参照),キーワードstruct,その構造体に名前を与
える≪識別子≫,省略可能な≪型仮引数並び≫(25.1.1参照),省略可能な≪構造体インタフェース群≫指
定(18.1.2参照),省略可能な≪型仮引数制約群節群≫(25.7参照),≪構造体本体≫(18.1.3参照),省略
可能なセミコロンの順で構成される。
構造体宣言が≪型仮引数並び≫をもたない場合,構造体宣言は≪型仮引数制約群節群≫をもっていては
ならない。
≪型仮引数並び≫をもつ構造体宣言は,総称構造体宣言(25.2参照)とする。
18.1.1 構造体修飾子
≪構造体宣言≫は構造体修飾子の列を含むことができる。
≪構造体修飾子群≫:
≪構造体修飾子≫
≪構造体修飾子群≫ ≪構造体修飾子≫
≪構造体修飾子≫:
new
public
protected
internal
369
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
private
一つの構造体宣言内で同じ修飾子を複数回使用すると,コンパイル時エラーになる。
構造体宣言の修飾子はクラス宣言(17.1.1参照)の修飾子と同様の意味をもつ。
18.1.2 構造体インタフェース
構造体の宣言には,≪構造体インタフェース群≫指定を含めることができる。この場合,構造体は指定
のインタフェース型を実装するという。
≪構造体インタフェース群≫:
: ≪インタフェース型並び≫
部分構造体宣言(17.1.4参照)の多重部分についてインタフェースの取扱いについては,17.1.2.2で詳細
に議論する。
インタフェース実装は20.4で詳細に規定する。
18.1.3 構造体本体
構造体の≪構造体本体≫は,構造体のメンバを定義する。
≪構造体本体≫:
{ ≪構造体メンバ宣言群≫opt }
18.2 構造体メンバ
構造体のメンバは,その構造体の≪構造体メンバ宣言群≫で導入されるメンバ及び
System.ValueType型から継承されるメンバで構成される。
≪構造体メンバ宣言群≫:
≪構造体メンバ宣言≫
≪構造体メンバ宣言群≫ ≪構造体メンバ宣言≫
≪構造体メンバ宣言≫:
≪定数宣言≫
≪フィールド宣言≫
≪メソッド宣言≫
≪特性宣言≫
≪イベント宣言≫
≪添字子宣言≫
≪演算子宣言≫
≪構築子宣言≫
≪静的構築子宣言≫
≪型宣言≫
≪終了化子宣言≫を除く,すべての種類の≪クラスメンバ宣言群≫は≪構造体メンバ宣言群≫でもある。
18.3に示す相違を除いて,17.2〜17.11で規定したクラスメンバの記述が構造体メンバにも適用される。
18.3 クラスと構造体との相違
18.3.1 値文脈
構造体は値型(11.1参照)であり,値の意味をもつ。これに対してクラスは参照型(11.2参照)であり,
参照の意味をもつ。
構造体型の変数は構造体のデータを直接格納する。クラス型の変数はデータへの参照を格納する。後者
はオブジェクトと呼ばれる。
370
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
クラスでは,二つの変数が同じオブジェクトを参照できるため,一方の変数に対する演算が他方の変数
によって参照されるオブジェクトに影響を与えることがある。構造体では,各々の変数がそれぞれデータ
のコピーをもち,ある変数に対する演算が別の変数に影響を与えることはない。さらに,構造体は参照型
ではないので,構造体型の値をnullにすることはできない。
例
struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
上記の定義が与えられている場合,次のコードは10を表示する。
Point a = new Point(10, 10);
Point b = a;
a.x = 100;
System.Console.WriteLine(b.x);
aからbへの代入は値のコピーを生成するので,a.xへの代入はbには影響を与えない。Point
を構造体ではなくクラスとして宣言した場合,aとbは同じオブジェクトを参照するので,出力
は100となる。
18.3.2 継承
すべての構造体型は,クラスobjectを継承したSystem.ValueTypeを暗黙に継承する。構造体の宣
言は,実装されるインタフェースの並びを規定できるが,構造体の宣言で基底クラスを指定することはで
きない。
構造体型は抽象にはならず,常に暗黙に封印される。したがってabstract及びsealed修飾子は構造
体宣言では使用できない。
構造体に対しては継承がないので,構造体メンバに対して宣言されたアクセス可能性がprotected又
はprotected internalであってはならない。
構造体の関数メンバはabstract又はvirtualであってはならず,override修飾子は
System.ValueType型から継承するメソッドを上書きすることだけが許される。
18.3.3 代入
構造体型の変数への代入では,代入対象の値のコピーを生成する。これは,クラス型の変数への代入と
は異なる。クラス型変数への代入では参照はコピーするが,参照によって識別されるオブジェクトはコピ
ーしない。
代入と同様に,構造体が値仮引数として渡される場合,又は,関数メンバの結果として返される場合は,
構造体のコピーが生成される。構造体は,ref仮引数又はout仮引数を使用する関数メンバに対しては,
参照渡しとなる。
構造体の特性又は添字子が代入の代入対象である場合は,その特性又は添字子アクセスに関連付けられ
たインスタンス式は,変数として分類する必要がある。インスタンス式が値に分類される場合は,コンパ
371
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
イル時エラーになる。これについては14.14.1で詳しく規定する。
18.3.4 省略時の値
12.2に示したとおり,変数の種類によっては生成される際に省略時の値に初期化されることがある。ク
ラス型の変数及び参照型の変数の省略時の値はnullとなる。構造体はnullに設定できない値型なので,
構造体の省略時の値は,すべての値型フィールドをその省略時の値に設定し,すべての参照型フィールド
をnullに設定する。
例 18.3.1で宣言された構造体Pointについて説明する。
Point[] a = new Point[100];
この例では配列中の各々のPointはフィールドx及びyを0に設定して生成される。
構造体の省略時の値は構造体の省略時構築子(11.1.1参照)が返す値に一致する。クラスとは異なり,
構造体は引数なしのインスタンス構築子を宣言できない。各構造体は,引数なしのインスタンス構築子を
暗黙に保有している。この構築子は,すべての値型フィールドをその省略時の値に設定し,すべての参照
型フィールドをnullに設定して生成される値を常に返す。
注記 省略時の初期化状態を正当な状態であるとみなすように構造体を設計することを推奨する。次
に例を示す。
using System;
struct KeyValuePair
{
string key;
string value;
public KeyValuePair(string key, string value) {
if (key == null || value == null) throw new ArgumentException();
this.key = key;
this.value = value;
}
}
この利用者定義のインスタンス構築子は,明示的に呼び出された場合にだけ,null値が設
定されないように保護する。KeyValuePair変数が省略時の値で初期化される場合には,key
フィールド及びvalueフィールドはnull値になるので,この構造体では,その状態を扱える
よう準備することが望ましい。
18.3.5 ボックス化及びボックス化解除
クラス型の値は,コンパイル時に参照を別の型として扱うだけで,型object又はそのクラスによって
実装されるインタフェース型に変換できる。同様に,型objectの値又はインタフェース型の値は,参照
を変更しなくてもクラス型に再変換できる。ただし,この場合は実行時の型検査が当然必要になる。
構造体は参照型ではないため,これらの処理は構造体型に対しては実装が異なる。構造体型の値を型
object又は構造体によって実装されるインタフェース型に変換する場合は,ボックス化処理が発生する。
同様に,型objectの値又はインタフェース型の値を構造体型に再変換する場合は,ボックス化解除処理
が発生する。クラス型に対するボックス化処理及びボックス化解除処理との主な相違点は,ボックス化に
よって,ボックス化されたインスタンス内に構造体の値がコピーされることと,ボックス化解除によって,
ボックス化されたインスタンスから構造体の値がコピーされることにある。
372
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
注記 したがって,ボックス化処理又はボックス化解除処理によって,ボックス化解除された構造体
に加えられる変更はボックス化された構造体には反映されない。
ボックス化及びボックス化解除の詳細については11.3を参照。
18.3.6 thisの意味
クラスのインスタンス構築子又はインスタンス関数メンバ内では,thisは値として分類される。この
ため,thisを使うと,関数メンバを呼び出したインスタンスを参照できるが,クラスの関数メンバ内で
thisに対する代入はできない。
構造体のインスタンス構築子の中では,thisは,構造体型のout仮引数に相当し,構造体のインスタ
ンス関数メンバの中では,thisは,構造体型のref仮引数に相当する。いずれの場合もthisは変数に
分類され,thisに対する代入を行うこと,又はthisをref若しくはout仮引数として渡すことによっ
て,呼び出された関数メンバの構造体全体を変更することができる。
18.3.7 フィールド初期化子
18.3.4で規定したように,構造体の省略時の値は,すべての値型フィールドをその省略時の値に設定し,
すべての参照型のフィールドをnullに設定した結果の値で構成される。このため,構造体では変数初期
化子を含むインスタンスフィールド宣言を使用することはできない。
例 したがって,次の例は一つ以上のコンパイル時エラーになる。
struct Point
{
public int x = 1; // Error, initializer not permitted
public int y = 1; // Error, initializer not permitted
}
この制限はインスタンスフィールドにだけ適用される。構造体の静的フィールドについては変数初期化
子を含めることができる。
18.3.8 構築子
クラスとは異なり,構造体では引数なしのインスタンス構築子を宣言できない。各構造体は引数なしの
インスタンス構築子を暗黙に保有する。この構築子は,すべての値型フィールドをその省略時の値に設定
し,すべての参照型フィールドをnullに設定することで生成される値を常に返す。構造体は,仮引数を
もつインスタンス構築子を宣言できる。
例 次に例を示す。
struct Point
{
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
上記のように宣言すると,次のいずれの文も0に初期化されたx及びyをもつPoint型のイ
ンスタンスを生成する。
Point p1 = new Point();
373
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Point p2 = new Point(0, 0);
構造体インスタンス構築子はbase(≪実引数並び≫opt)形式の構築子初期化子を含んではならない。
構造体インスタンス構築子のthis変数は構造体型のout仮引数に相当し,out仮引数と同様に,構築
子が処理を戻すすべての場所で,thisは確実に代入されていなければならない(12.3参照)。
例 次に示すインスタンス構築子の実装を使って説明する。
struct Point
{
int x, y;
public int X {
set { x = value; }
}
public int Y {
set { y = value; }
}
public Point(int x, int y) {
X = x;
// error, this is not yet definitely
assigned
Y = y;
// error, this is not yet definitely
assigned
}
}
インスタンスメンバ関数(特性X及びYに対するアクセス子を含む)は,構造体のすべての構
築済みのフィールドが確実に代入されるまで呼び出すことができない。ただし,Pointが構造体
ではなくクラスであった場合は,上のインスタンス構築子の実装は許可される。
18.3.9 終了化子
構造体に終了化子を宣言することは許されていない。
18.3.10 静的構築子
構造体の静的構築子は,クラスの静的構築子とおおむね同じ規則に従う。構造体の静的構築子の実行は,
アプリケーション領域内で起こった次のうちの最初のイベントによって引き起こされる。
− 構造体のインスタンスメンバが参照された。
− 構造体の静的メンバが参照された。
− 構造体の明示的に宣言された構築子が呼び出された。
注記 構造体型の省略時の値(18.3.4参照)の生成は静的構築子を実行するきっかけにはならない(こ
の例には配列要素の初期値がある。)。
19 配列
配列とは,算出された添字を用いてアクセスされる複数の変数を含むデータ構造とする。配列に含まれ
る変数は,配列の要素とも呼ばれ,すべて同一型とする。この型を配列の要素型と呼ぶ。
配列は,配列中の各々の要素に関連付けられた添字の個数を決定する位階をもつ。配列の位階は,配列
の次元数とも呼ばれる。位階が1の配列は1次元配列と呼ばれる。位階が1を超える配列は多次元配列と
374
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
呼ばれる。特定の大きさの多次元配列は2次元配列,3次元配列などと呼ばれる。配列の各次元には,関
連付けられた長さがある。この長さは0以上の整数とする。次元の長さは配列の型には含まれないが,実
行時に配列型のインスタンスが生成されるときに確立される。次元の長さはその次元に対する有効な添字
の範囲を決定する。すなわち,長さNの次元に対しては,添字は0以上N ‒ 1以下の範囲とする。配列
の要素の総数は,配列の各次元の長さの積とする。配列に長さが0の次元が存在する場合,配列は空とす
る。
配列の要素型は,任意の型に設定できるので,配列型でもよい。
19.1 配列型
配列型は,≪非配列型≫の後に一つ以上の≪位階指定子≫が続く形式で記述される。
≪配列型≫:
≪非配列型≫ ≪位階指定子≫
≪非配列型≫:
≪値型≫
≪クラス型≫
≪インタフェース型≫
≪委譲型≫
≪型仮引数≫
≪位階指定子群≫:
≪位階指定子≫
≪位階指定子群≫ ≪位階指定子≫
≪位階指定子≫:
[ ≪次元区切り子群≫opt ]
≪次元区切り子群≫:
,
≪次元区切り子群≫ ,
≪非配列型≫は,それ自体が≪配列型≫ではない≪型≫とする。
配列型の位階は,≪配列型≫内で最も左の≪位階指定子≫によって指定される。≪位階指定子≫は,そ
の配列が,≪位階指定子≫内の“,”字句の個数に1を足した位階をもつ配列であることを示す。
配列型の要素型は,最も左の≪位階指定子≫を取り除いた型とする。
− T[R]形式の配列型は位階がRで非配列要素型がTの配列とする。
− T[R][R1]…[RN]形式の配列は位階がRで要素型がT[R1]…[RN]の配列とする。
≪位階指定子≫は,実際には,最後の非配列要素型の前まで,英語だと左から右,日本語だと右から左
に読み上げられる。
例 型int[][,,][,]はintの2次元配列の3次元配列の1次元配列とする。
実行時には,配列型の値はnull又はその配列型若しくは19.5で説明する共変配列型のインスタンスへ
の参照とする。
19.1.1 System.Array型
System.Array型はすべての配列型の抽象基底型とする。任意の配列型からSystem.Arrayへの暗黙
の参照変換(13.1.4参照)及びSystem.Arrayを実装する任意のインタフェース型への暗黙の参照変換
(13.1.4参照)が存在する。System.Arrayから任意の配列型への明示的な参照変換(13.2.3参照)及び
375
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
System.Arrayを実装する任意のインタフェース型から任意の配列型への明示的な参照変換(13.2.3参照)
が存在する。System.Array自体は≪配列型≫ではない。System.Arrayは,すべての≪配列型≫の派
生元である≪クラス型≫とする。
実行時に,System.Array型の値はnull又は任意の配列型への参照となり得る。
19.2 配列の生成
配列インスタンスは,≪配列生成式≫(14.5.10.2参照)若しくは≪配列初期化子≫(19.7参照)を含む
フィールド又は局所変数宣言によって明示的に生成される。配列インスタンスは,その展開形式(14.4.1
参照)の中のメソッドを呼び出すことによって,暗黙に生成されることもある。
配列インスタンスが生成されるときには,各次元の位階及び長さが確立され,インスタンスの有効期間
を通じて変わらない。すなわち,既存の配列インスタンスの位階は変更できず,次元の長さも変更できな
い。
配列インスタンスは常に配列型とする。System.Array型は具現化できない抽象型とする。
≪配列生成式≫によって生成された配列の要素は,常にその省略時の値(12.2参照)で初期化される。
19.3 配列要素へのアクセス
配列要素にはA[I1, I2, …, IN]形式の≪要素アクセス≫式(14.5.6.1参照)を使ってアクセスする。こ
こで,Aは配列型の式であり,各々のIXはint,uint,long,ulong又はこれらの型のうちの一つ以上
の型に暗黙に変換される型の式とする。配列要素へのアクセスの結果は,変数,すなわち添字によって選
択された配列要素とする。
配列の要素はforeach文(15.8.4参照)を使って列挙できる。
19.4 配列メンバ
すべての配列型はSystem.Array型によって宣言されたメンバを継承する。
19.5 配列の共変性
任意の二つの≪参照型≫A及びBについて,AからBへの暗黙の参照変換(13.1.4参照)又は明示的な
参照変換(13.2.3参照)が存在するならば,配列型A[R]から配列型B[R]への同様の参照変換も存在する。
ここでRは指定された任意の≪位階指定子≫(ただし両方の配列型に対して同じもの)とする。この関係
を配列の共変性と呼ぶ。特に,BからAへの暗黙の参照変換が存在する場合,配列の共変性は配列型A[R]
の値が実際には配列型B[R]のインスタンスへの参照になっている可能性があることを意味する。
配列の共変性によって,参照型配列の要素への代入は,その配列要素に代入しようとしている値が実際
に許容される型(14.13.1参照)かどうかの実行時検査を含む。
例 次に例を示す。
class Test
{
static void Fill(object[] array, int index, int count, object value)
{
for (int i = index; i < index + count; i++) array[i] = value;
}
static void Main() {
string[] strings = new string[100];
Fill(strings, 0, 100, "Undefined");
Fill(strings, 0, 10, null);
376
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Fill(strings, 90, 10, 0);
}
}
メソッドFillのarray[i]への代入は,valueによって参照されるオブジェクトがnull又
はarrayの実際の要素型と互換性のある型かどうかを確かめる,暗黙の実行時検査を含む。Main
中の最初の二つのFillの呼出しは成功するが,3番目の呼出しはarray[i]への最初の代入の
際にSystem.ArrayTypeMismatchExceptionが送出される。この例外はボックス化された
intがstringの配列に格納できないために発生する。
配列の共変性は≪値型≫の配列には適用されない。例えば,int[]をobject[]として扱うことを許す
変換は存在しない。
19.6 配列と総称IListインタフェース
一次元配列S[]はインタフェースSystem.Collections.Generic.IList<S>(IList<S>と略記)
及びその基底インタフェースを実装する。そのため,S[]からIList<S>及びその基底インタフェースへ
の暗黙の変換が存在する。さらに,SからTへの暗黙の参照変換が存在する場合,S[]はIList<T>を実
装し,S[]からIList<T>及びその基底インタフェースへの暗黙の参照変換が存在する(13.1.4参照)。S
からTへの明示的な参照変換がある場合,S[]からIList<T>及びその基底インタフェースへの明示的な
参照変換が存在する(13.2.3参照)。
例
using System.Collections.Generic;
class Test
{
static void Main() {
string[] sa = new string[5];
object[] oa1 = new object[5];
object[] oa2 = sa;
IList<string> lst1 = sa; // OK
IList<string> lst2 = oa1; // Error ‒ need cast
IList<object> lst3 = sa; // OK
IList<object> lst4 = oa1; // OK
IList<string> lst5 = (IList<string>)oa1; // Exception
IList<string> lst6 = (IList<string>)oa2; // OK
}
}
この例で,代入lst2 = oa1は,object[]からIList<string>への変換が暗黙の変換ではなく明示
的な変換であるため,コンパイル時エラーを生じる。キャスト(IList<string>)oa1は,oa1が
string[]ではなくobject[]を参照しているため,実行時に例外を送出する原因になるだろう。しかし,
キャスト(IList<string>)oa2は,oa2がstring[]を参照しているため,例外を送出する原因になら
ないだろう。
S[]からIList<T>への暗黙的な又は明示的参照変換が存在する場合はいつでも,IList<T>及びその
基底インタフェースからS[]への明示的な参照変換も存在する(13.2.3参照)。
377
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
配列型S[]がIList<T>を実装しているとき,実装されたインタフェースのメンバの中には例外を送出
するものがあってもよい。インタフェースの実装の正確な動作は,この規格の範囲外とする。
19.7 配列初期化子
配列初期化子は,フィールド宣言(17.4参照),局所変数宣言(15.5.1参照)及び配列生成式(14.5.10.2
参照)の中で指定できる。
≪配列初期化子≫:
{ ≪変数初期化子並び≫opt }
{ ≪変数初期化子並び≫ , }
≪変数初期化子並び≫:
≪変数初期化子≫
≪変数初期化子並び≫ , ≪変数初期化子≫
≪変数初期化子≫:
≪式≫
≪配列初期化子≫
注記 C及びC++と同様に,C#は,≪変数初期化子並び≫の末尾にコンマが来ることを許す。この構
文は,≪変数初期化子並び≫の要素の追加削除に関する柔軟性を高め,機械的に生成する場合
の作業を単純化する。
配列初期化子は,字句“{”及び“}”で取り囲まれ,字句“,”で区切られた変数初期化子の並びで構
成される。各変数初期化子は,式又は入れ子になった配列初期化子(多次元配列の場合)とする。
配列初期化子が使用される文脈によって,初期化される配列の型が決まる。配列生成式では,配列型が
初期化子の直前に置かれる。フィールド又は変数の宣言では,配列型は,宣言するフィールド又は変数の
型になる。配列初期化子がフィールド又は変数宣言中で使用される場合
例
int[] a = {0, 2, 4, 6, 8};
上記の配列生成式は,次の簡略記法によるものと等価とする。
例
int[] a = new int[] {0, 2, 4, 6, 8};
1次元配列の場合の配列初期化子は,配列の要素型と代入互換な式の列で構成する必要がある。各式は,
添字0の要素から昇順に,配列要素を初期化する。配列初期化子内の式の個数によって,生成される配列
インスタンスの長さが決まる。
例 上記の配列初期化子は長さ5のint[]インスタンスを生成し,そのインスタンスを次の値で初期
化する。
a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
多次元配列の場合,配列初期化子は,配列の次元と同じだけの入れ子レベルをもつ必要がある。最外側
の入れ子レベルが左端の次元に対応し,最内側の入れ子レベルが右端の次元に対応する。配列の各次元の
長さは,配列初期化子内の対応する入れ子レベルに存在する要素の個数によって決まる。入れ子になった
配列初期化子の場合,それぞれの要素数は,同じレベルのほかの配列初期化子と同じ個数である必要があ
る。
例
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
378
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
この例では,次に示すように,左端の次元の長さが5で右端の次元の長さが2の2次元配列が
生成される。
int[,] b = new int[5, 2];
そして,次の値で配列初期化子が初期化される。
b[0, 0] = 0; b[0, 1] = 1;
b[1, 0] = 2; b[1, 1] = 3;
b[2, 0] = 4; b[2, 1] = 5;
b[3, 0] = 6; b[3, 1] = 7;
b[4, 0] = 8; b[4, 1] = 9;
配列生成式に明示的な次元の長さと配列初期化子が含まれる場合,その長さは定数式である必要がある。
また,それぞれの入れ子レベルにある要素の個数は,対応する次元の長さと一致する必要がある。
例 次に例を示す。
int i = 3;
int[] x = new int[3] {0, 1, 2};
// OK
int[] y = new int[i] {0, 1, 2};
// Error, i not a constant
int[] z = new int[3] {0, 1, 2, 3};
// Error, length/initializer
mismatch
この例では,yに対する初期化子は,次元の長さの式が定数ではないため,コンパイル時エラ
ーになる。また,zに対する初期化子は,初期化子中の要素数と長さが一致していないため,コ
ンパイル時エラーになる。
注記 標準C++と同様に,C#では,≪配列初期化子≫の終わりに余分なコンマがあってもよい。
この文法は,変数初期化子並びのメンバの追加及び削除に柔軟性を与え,変数初期化子並
びの機械生成を簡単にする。
20 インタフェース
インタフェースは契約(呼出し情報の集合)を定義する。インタフェースを実装するクラス又は構造体
は,その契約に従わなければならない。インタフェースは複数の基底インタフェースから継承することが
できる。また,クラス又は構造体は複数のインタフェースを実装することができる。
インタフェースには,メソッド,特性メンバ,イベント及び添字子を含むことができる。インタフェー
ス自体は,インタフェースが宣言するメンバの実装を提供しない。インタフェースは,そのインタフェー
スを実装しなければならないクラス又は構造体が提供するメンバを単に指定するだけとする。
20.1 インタフェース宣言
≪インタフェース宣言≫は,新しいインタフェース型を宣言するための≪型宣言≫(16.6参照)とする。
≪インタフェース宣言≫:
≪属性群≫opt ≪インタフェース修飾子群≫opt partialopt interface ≪識別
子≫ ≪型仮引数並び≫opt ≪インタフェース基底≫opt ≪型仮引数制約群節群≫opt
≪インタフェース本体≫ ;opt
≪インタフェース宣言≫の構成要素としては,省略可能な一つ以上の≪属性群≫(箇条24参照)があり,
その後は順に,省略可能な一つ以上の≪インタフェース修飾子群≫(20.1.1参照),省略可能なpartial
修飾子(17.1.4参照),キーワードinterface,インタフェースの名前に当たる≪識別子≫,省略可能な
379
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪型仮引数並び≫(25.1.1参照),省略可能な一つ以上の≪インタフェース基底≫(20.1.2参照),省略可
能な≪型仮引数制約群≫(25.7参照),≪インタフェース本体≫(20.1.3参照),更に省略可能なセミコロ
ンが続く。
≪型仮引数並び≫を提供するインタフェース宣言は,総称インタフェース宣言である(25.3参照)。
インタフェース宣言は,≪型仮引数並び≫を提供することなしに,≪型仮引数制約群≫を提供してはな
らない。
20.1.1 インタフェース修飾子
≪インタフェース宣言≫には,インタフェース修飾子を並べることができる。
≪インタフェース修飾子群≫:
≪インタフェース修飾子≫
≪インタフェース修飾子群≫ ≪インタフェース修飾子≫
≪インタフェース修飾子≫:
new
public
protected
internal
private
一つのインタフェース宣言内で同じ修飾子を複数回使用すると,コンパイル時エラーになる。
new修飾子は,入れ子インタフェースでだけ利用できる。これによって,インタフェースは継承された
同一名のインタフェースを隠ぺいする(17.2.2で規定)。
public,protected,internal及びprivateの修飾子は,インタフェースのアクセス可能性を制
御する。インタフェース宣言を行う状況によって,どの修飾子が利用できるか決まる(10.5.1参照)。部分
型宣言(17.1.4参照)がアクセス可能性規定(public,protected,internal及びprivateの修飾子)
を含む場合は,17.1.1の規則を適用する。
20.1.2 基底インタフェース
インタフェースは1個以上のインタフェースを継承できる。継承元のインタフェースは,インタフェー
スの“明示的な基底インタフェース”と呼ばれる。あるインタフェースが一つ以上の明示的な基底インタ
フェースをもつ場合,そのインタフェースの宣言では,インタフェース識別子の後に,コロン及びコンマ
区切りの基底インタフェース識別子の並びが続く。
≪インタフェース基底≫:
: ≪インタフェース型並び≫
明示的な基底インタフェースは,構築インタフェース型(25.5参照)になることができる。基底インタ
フェースは,有効範囲にある型仮引数を伴うことができるが,基底インタフェースそれ自身は,型仮引数
になることはできない。
明示的な基底インタフェース(及び,それに伴う型仮引数)は,アクセス可能性が継承インタフェース
と少なくとも同じでなければならない。
注記 例えば,publicインタフェースの≪インタフェース基底≫に,private又はinternalな
インタフェースを指定すると,コンパイル時エラーになる。
インタフェースが,直接的又は間接的に,そのインタフェース自体から継承する場合もコンパイル時エ
ラーになる。
380
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
あるインタフェースの基底インタフェースとは,明示的な基底インタフェース及びその基底インタフェ
ース(構築総称型の型仮引数が型実引数に置き換わった後)とする。すなわち,あるインタフェースがも
つすべての基底インタフェースとは,その明示的な基底インタフェース及びそれらの明示的な基底インタ
フェースなどの完全推移的閉包となる。インタフェースは,その基底インタフェースのすべてのメンバを
継承する。
例 次に例を示す。
interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
interface IListBox: IControl
{
void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}
この例では,IComboBoxの基底インタフェースは,IControl,ITextBox及びIListBox
である。すなわち,上記のIComboBoxインタフェースは,Paintだけでなく,SetText及び
SetItemsもメンバとしてもつ。
構築総称型から継承したメンバは,型が代入された後に継承される。すなわち,メンバの成分である型
は,基底インタフェース宣言の型仮引数が,≪インタフェース基底≫の指定で使用された型実引数で置き
換わったものである。
例 次に例を示す。
interface IBase<T>
{
T[] Combine(T a, T b);
}
interface IDerived : IBase<string[,]>
{
// Inherited: string[][,] Combine(string[,] a, string[,] b);
}
このインタフェースIDrivedは,型仮引数Tがstring[,]で置き換わった後,Combine メソッドを
継承する。
インタフェースを実装するクラス及び構造体も,暗黙にそのインタフェースの基底インタフェースのす
べてを実装する。
部分インタフェース(17.1.4参照)の各部分でのインタフェースの取扱いは,17.1.2.2で詳しい規定があ
る。
381
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
20.1.3 インタフェース本体
インタフェースの≪インタフェース本体≫は,インタフェースのメンバを定義する。
≪インタフェース本体≫:
{ ≪インタフェースメンバ宣言群≫opt }
20.2 インタフェースメンバ
インタフェースのメンバは,基底インタフェースから継承したメンバ及びそのインタフェース自体が宣
言したメンバとする。
≪インタフェースメンバ宣言群≫:
≪インタフェースメンバ宣言≫
≪インタフェースメンバ宣言群≫ ≪インタフェースメンバ宣言≫
≪インタフェースメンバ宣言≫:
≪インタフェースメソッド宣言≫
≪インタフェース特性宣言≫
≪インタフェースイベント宣言≫
≪インタフェース添字子宣言≫
インタフェース宣言では,0個以上のメンバを宣言できる。インタフェースのメンバは,メソッド,特
性メンバ,イベント,又は添字子でなければならない。インタフェースは,定数,フィールド,演算子,
インスタンス構築子,終了化子,又は型をメンバとしてもつことはできない。また,インタフェースには,
どのような種類の静的メンバも含めることができない。
すべてのインタフェースメンバは,暗黙に公開アクセスとなる。インタフェースメンバの宣言に修飾子
を含めるとコンパイル時エラーになる。インタフェースメンバは,abstract,public,protected,
internal,private,virtual,override又はstaticを付けて宣言できない。
例 次に例を示す。
public delegate void StringListEventHandler(IStringList sender,
ListEventArgs e);
public interface IStringList
{
void Add(string s);
int Count { get; }
event StringListEventHandler Changed;
string this[int index] { get; set; }
}
この例では,可能なメンバの種類を一つずつ含んだインタフェースを宣言している。すなわち,
メソッド,特性メンバ,イベント及び添字子である。
≪インタフェース宣言≫は,新しい宣言空間を生成する。≪インタフェース宣言≫に直接含まれる型仮
引数及び≪インタフェースメンバ宣言≫は,この宣言空間に新しいメンバを導入する。≪インタフェース
メンバ宣言≫には次の規則が適用される。
− インタフェースの≪型仮引数並び≫にある型仮引数の名前は,同じ≪型仮引数並び≫にある他の型仮
引数の名前と異なっていなければならず,また,インタフェースのすべてのメンバの名前と異ならな
ければならない。
382
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− メソッドの名前は,同一インタフェースで宣言されているすべての特性メンバ及びイベントの名前と
は異なる名前である必要がある。さらに,メソッドの呼出し情報(10.6参照)は,同じインタフェー
ス内に宣言された他のすべてのメソッドの呼出し情報と異なるものでなければならない。また,同じ
メソッドに宣言された二つのメソッドは,ref又はoutが違うだけの呼出し情報をもつことができな
い。
− 特性メンバ及びイベントの名前は,同一インタフェースで宣言されている他のすべてのメンバの名前
とは異なっていなければならない。
− 添字子の呼出し情報は,同一インタフェースで宣言されている他のすべての添字子の呼出し情報とは
異なっていなければならない。
インタフェースの継承されたメンバは,そのインタフェースの宣言空間には含まれない。このため,イ
ンタフェースは,継承されたメンバと同じ名前や呼出し情報をもつメンバを宣言できる。このような宣言
を行うとき,派生インタフェースのメンバが,基底インタフェースのメンバを“隠ぺいする”という。継
承されたメンバを隠ぺいしてもエラーとはみなされないが,コンパイラは警告を出す。この警告をなくす
には,派生したインタフェースメンバの宣言にnew修飾子を含めて,派生したメンバが基底メンバを隠ぺ
いすることを示さなければならない。この点については,10.7.1.2に詳しい規定がある。
もし,new修飾子が継承していないメンバを隠ぺいしない宣言の中に含まれると,その結果として警告
が出る。このnew修飾子を取り除けば,この警告をなくすことができる。
注記 クラスobject のメンバは,厳密にいえば,いずれのインタフェース(20.2参照)のメンバで
もない。しかし,クラスobjectのメンバは,いずれのインタフェース型でもメンバ検索を介
して利用することができる。
部分宣言(17.1.4参照)の一部として宣言されたインタフェースのメンバの詳細は,17.2 に規定する。
20.2.1 インタフェースのメソッド
インタフェースのメソッドは,≪インタフェースメソッド宣言≫を使って宣言する。
≪インタフェースメソッド宣言≫:
≪属性群≫opt newopt ≪返却値型≫ ≪識別子≫ ≪型仮引数並び≫opt ( ≪仮引数並
び≫opt ) ≪型仮引数制約群節群≫opt ;
インタフェースのメソッドの宣言における≪属性群≫,≪返却値型≫,≪識別子≫及び≪仮引数並び≫
は,クラスのメソッドの宣言(17.5参照)におけるそれらと同じ意味とする。インタフェースのメソッド
の宣言では,メソッド本体を指定できない。したがって,その宣言は常にセミコロンで終わる。
≪インタフェースメソッド宣言≫は,≪型仮引数並び≫opt がなければ,≪型仮引数制約群節群≫をも
ってはならない。
20.2.2 インタフェースの特性メンバ
インタフェースの特性メンバは,≪インタフェース特性宣言≫を使って宣言する。
≪インタフェース特性宣言≫:
≪属性群≫opt newopt ≪型≫ ≪識別子≫ { ≪インタフェースアクセス子群≫ }
≪インタフェースアクセス子群≫:
≪属性群≫opt get ;
≪属性群≫opt set ;
≪属性群≫opt get ; ≪属性群≫opt set ;
≪属性群≫opt set ; ≪属性群≫opt get ;
383
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
インタフェースの特性メンバの宣言における≪属性群≫,≪型≫及び≪識別子≫は,クラスの特性メン
バの宣言(17.6参照)におけるそれらと同じ意味とする。
インタフェースの特性メンバ宣言のアクセス子は,クラスの特性メンバ宣言のアクセス子に相当する。
ただし,アクセス子の本体は,常にセミコロンでなければならない。したがって,アクセス子は,特性メ
ンバが読み書き可能,読込み専用又は書出し専用のいずれであるかだけを示す。
20.2.3 インタフェースのイベント
インタフェースのイベントは,≪インタフェースイベント宣言≫を使って宣言する。
≪インタフェースイベント宣言≫:
≪属性群≫opt newopt event ≪型≫ ≪識別子≫ ;
インタフェースのイベント宣言における≪属性群≫,≪型≫及び≪識別子≫は,クラスのイベント宣言
(17.7参照)におけるそれらと同じ意味とする。
20.2.4 インタフェースの添字子
インタフェースの添字子は,≪インタフェース添字子宣言≫を使って宣言する。
≪インタフェース添字子宣言≫:
≪属性群≫opt newopt ≪型≫ this [ ≪仮引数並び≫ ] { ≪インタフェースアク
セス子群≫ }
インタフェースの添字子の宣言における≪属性群≫,≪型≫及び≪仮引数並び≫は,クラスの添字子の
宣言(17.8参照)におけるそれらと同じ意味とする。
インタフェースの添字子宣言のアクセス子は,クラスの特性メンバ宣言のアクセス子に相当する。ただ
し,アクセス子の本体は,常にセミコロンでなければならない。したがって,アクセス子は,添字子が読
み書き可能,読込み専用又は書出し専用のいずれであるかだけを示す。
20.2.5 インタフェースのメンバアクセス
インタフェースメンバは,I.MやI[A]という形式のような,メンバアクセス(14.5.4参照)表現や添字
子アクセス(14.5.6.2参照)表現を通じてアクセスされる。このときのIはインタフェース型の表現式で
あり,Mはインタフェースのメソッドや特性メンバ又はイベントであり,Aは添字子の実引数並びとする。
厳密な単一継承インタフェース(継承連鎖において,どのインタフェースも直接的な基底インタフェー
スがゼロ又は一つのインタフェース)にとって,メンバを検索する方法(14.3参照),メソッド呼出し
(14.5.5.1参照)及び添字子アクセス(14.5.6.2参照)の規則は,クラス及び構造体の場合と同じとする。
派生したメンバは,それよりも上位のメンバを同じ名前,同じ呼出し情報によって隠ぺいする。しかし,
多重継承インタフェースの場合は,二つ以上の無関係な基底インタフェースが同じ名前又は同じ呼出し情
報でメンバを宣言すると,あいまいさが生じることがある。ここでは,あいまいさが生じる例を幾つか示
す。すべての場合において,あいまいさを解決するために明示的なキャストを使用できる。
例1 次に例を示す。
interface IList
{
int Count { get; set; }
}
interface ICounter
{
void Count(int i);
384
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
interface IListCounter: IList, ICounter {}
class C
{
void Test(IListCounter x) {
x.Count(1);
// Error
x.Count = 1;
// Error
((IList)x).Count = 1;
// Ok, invokes IList.Count.set
((ICounter)x).Count(1);
// Ok, invokes ICounter.Count
}
}
最初の二つの文では,IListCounterのCountメンバの検索があいまいなため,コンパイ
ル時エラーになる。この例に示すように,該当する基底インタフェース型にxをキャストする
ことによって,あいまいさが解決される。このようなキャストには,実行時の負荷はない。こ
れらのキャストは,コンパイル時に,より上位の型のインスタンスとみなすためのものである。
例2 次に例を示す。
interface IInteger
{
void Add(int i);
}
interface IDouble
{
void Add(double d);
}
interface INumber: IInteger, IDouble {}
class C
{
void Test(INumber n) {
n.Add(1);
// Error, both Add methods are
applicable
n.Add(1.0);
// Ok, only IDouble.Add is applicable
((IInteger)n).Add(1); // Ok, only IInteger.Add is a
candidate
((IDouble)n).Add(1); // Ok, only IDouble.Add is a
candidate
}
}
n.Add(1)の呼出しはあいまいである。なぜなら,メソッド呼出し(14.5.5.1参照)では,す
べての多重定義されたメソッド候補が同一の型に宣言されなければならないためである。しか
し,n.Add(1.0)という呼出しは許される。なぜなら,IDouble.Addだけが適用可能だから
385
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
である。明示的なキャストを挿入すれば,メソッド候補は唯一になるので,あいまいさがなく
なる。
例3 次に例を示す。
interface IBase
{
void F(int i);
}
interface ILeft: IBase
{
new void F(int i);
}
interface IRight: IBase
{
void G();
}
interface IDerived: ILeft, IRight {}
class A
{
void Test(IDerived d) {
d.F(1);
// Invokes ILeft.F
((IBase)d).F(1);
// Invokes IBase.F
((ILeft)d).F(1);
// Invokes ILeft.F
((IRight)d).F(1);
// Invokes IBase.F
}
}
メンバIBase.Fは,メンバILeft.Fによって隠ぺいされる。したがって,IRightに派生
するパスではIBase.Fは隠ぺいされないが,この例のd.F(1)という呼出しでは,ILeft.F
のほうが選択される。
多重継承インタフェースにおける隠ぺいの規則は次による。メンバがいずれかのアクセスパ
スで隠ぺいされるならば,すべてのアクセスパスで隠ぺいされる。IDerivedからILeft,
IBaseへのパスでは,IBase.Fを隠ぺいしているので,このメンバはIDerivedからIRight,
IBaseへのパスでも隠ぺいされる。
20.3 完全限定されたインタフェースメンバ名
インタフェースメンバは,限定されたインタフェースメンバ名で参照されることがある。限定されたイ
ンタフェースメンバ名は,そのメンバを宣言するインタフェースの名前,ドット,そのメンバの名前の順
に構成される。
例 次に例を示す。
interface IControl
{
void Paint();
386
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
interface ITextBox: IControl
{
void SetText(string text);
}
この宣言の例では,Paintの限定されたインタフェースメンバ名はIControl.Paintであり,
SetTextの限定されたインタフェースメンバ名はITextBox.SetTextである。この例では,
ITextBox.PaintのようにPaintを参照できない。
インタフェースが名前空間に含まれる場合,限定されたインタフェースメンバ名はその名前空間名を含
む。
例 次に例を示す。
namespace System
{
public interface ICloneable
{
object Clone();
}
}
ここでは,Cloneの完全限定名は,System.IClonable.Cloneである。
20.4 インタフェースの実装
インタフェースは,クラス及び構造体によって実装されることができる。クラス又は構造体がインタフ
ェースを実装していることを示すために,そのクラス又は構造体の基底クラスの並びにインタフェース(≪
識別子≫,又は≪識別子≫及び≪型仮引数並び≫)が含まれる。
例 次に例を示す。
interface ICloneable
{
object Clone();
}
interface IComparable
{
int CompareTo(object other);
}
class ListEntry: ICloneable, IComparable
{
public object Clone() {…}
public int CompareTo(object other) {…}
}
インタフェースを実装するクラス又は構造体は,そのインタフェースの基底インタフェースのすべての
メンバを暗黙に実装する。クラス又は構造体が,基底クラスの並び内にすべての基底インタフェースを明
示的に挙げていない場合でも,この暗黙の実装は行われる。
387
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例 次に例を示す。
interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
class TextBox: ITextBox
{
public void Paint() {…}
public void SetText(string text) {…}
}
ここでは,TextBoxはIControlとITextBoxの両方を実装している。
20.4.1 明示的なインタフェースメンバの実装
インタフェースを実装するために,クラス又は構造体は,明示的インタフェースメンバ実装を宣言する
ことができる。明示的インタフェースメンバ実装とは,限定されたインタフェースメンバ名を参照する,
メソッド,特性メンバ,イベント又は添字子の宣言とする。
例1 次に例を示す。
interface ICloneable
{
object Clone();
}
interface IComparable
{
int CompareTo(object other);
}
class ListEntry: ICloneable, IComparable
{
object ICloneable.Clone() {…}
int IComparable.CompareTo(object other) {…}
}
ここでは,ICloneable.CloseやIComparable.CompareToが明示的なインタフェース
メンバの実装である。
例2 場合によっては,インタフェースメンバの名前が,インタフェースメンバを実装するクラスに
適していないことがある。そのような場合は,明示的インタフェースメンバの実装を用いてイ
ンタフェースメンバを実装することができる。例えば,ファイルを抽象化したものを実装する
クラスでは,ファイル資源を解放する作用のあるCloseメンバ関数だけでなく,IDisposable
インタフェースのDisposeメソッドを明示的なインタフェースメンバの実装を使って実装す
388
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ることが多い。
interface IDisposable {
void Dispose();
}
class MyFile: IDisposable
{
void IDisposable.Dispose() {
Close();
}
public void Close() {
// Do what's necessary to close the file
System.GC.SuppressFinalize(this);
}
}
メソッドの呼出し,特性メンバのアクセス,イベントメンバのアクセス,又は添字子アクセスにおいて,
限定されたインタフェースメンバ名で,明示的なインタフェースメンバの実装にアクセスすることはでき
ない。明示的なインタフェースメンバの実装にアクセスできるのは,インタフェース型のインスタンスか
らだけとする。また,その場合は単にメンバ名で参照できる。
明示的なインタフェースメンバの実装にextern(17.5参照)以外の修飾子を含めると,コンパイル時
エラーになる。明示的なインタフェースメソッドの実装に≪型仮引数制約群≫を含めると,コンパイル時
エラーになる。明示的な総称インタフェースメソッドの実装の制約は,インタフェースメソッドから継承
するものとする。
明示的なインタフェースメンバの実装には,他のメンバとは異なるアクセス可能性の特質がある。明示
的なインタフェースメンバの実装は,メソッドの呼出し及び特性メンバアクセスにおいて,限定されたイ
ンタフェースメンバ名ではアクセスできない。このため,アクセスはある意味では非公開である。ただし,
インタフェース型のインスタンスからはアクセスできるため,アクセスはある意味で公開でもある。
明示的なインタフェースメンバの明示的実装には,主に次の二つの目的がある。
− 明示的なインタフェースメンバの実装はクラス及び構造体のインスタンスからアクセスできないため,
インタフェース実装を,クラス及び構造体の公開インスタンスから除外できる。クラス及び構造体が,
そのクラス及び構造体の消費者に関係のない内部インタフェースを実装する場合は,この機能が役立
つ。
− 明示的なインタフェースメンバの実装では,同じ呼出し情報をもつインタフェースメンバを明確に区
別できる。明示的なインタフェースメンバの実装を行わない場合,クラス及び構造体が,同じ呼出し
情報と返却値の型をもつインタフェースメンバの異なる実装をもつことはできない。同様に,クラス
及び構造体が,同じ呼出し情報と異なる返却値の型をもつインタフェースメンバの実装をもつことは
できない。
明示的なインタフェースメンバの実装を有効にするには,クラス及び構造体の定義において,明示的に
実装されるインタフェースメンバの,そのインタフェース名,名前,型,型仮引数の数及び仮引数型と正
確に一致する,インタフェース名,名前,型,型仮引数の数及び仮引数型をもつメンバを含むインタフェ
ースを,基底クラスの並び内に指定しなければならない。インタフェースの関数メンバが,仮引数配列を
389
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
もつとき,その明示的なインタフェースメンバ実装の対応する仮引数は,params 修飾子を付けることが
必す(須)ではないが,これを付けることができる。インタフェースの関数メンバが仮引数配列をもたな
いならば,その明示的なインタフェースメンバの実装は,仮引数配列をもってはならない。
例 次に例を示す。
class Shape: ICloneable
{
object ICloneable.Clone() {…}
int IComparable.CompareTo(object other) {…}
// invalid
}
IComparable.Cloneの宣言は,コンパイル時エラーになる。なぜなら,IComparableが
Shapeクラスの基底クラスの並びに列挙されず,また,IComparableはICloneableの基底
インタフェースではないからである。同様に,次の宣言を考えてみる。
class Shape: ICloneable
{
object ICloneable.Clone() {…}
}
class Ellipse: Shape
{
object ICloneable.Clone() {…}
// invalid
}
EllipseクラスのICloneable.Cloneの宣言は,コンパイル時エラーになる。なぜなら,
ICloneableがEllipseの基底クラスの並びに列挙されていないからである。
限定されたインタフェースメンバ名は,そのメンバを宣言したインタフェースを参照しなければならな
い。
例 次に例を示す。
interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
class TextBox: ITextBox
{
void IControl.Paint() {…}
void ITextBox.SetText(string text) {…}
}
Paintの明示的なインタフェースメンバの実装は,ITextBox.Paintとしてではなく,
IControl.Paintとして記述しなければならない。
390
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
20.4.2 インタフェース写像
クラス又は構造体は,そのクラス及び構造体の基底クラスの並びに含まれるインタフェースのすべての
メンバの実装を提供しなければならない。インタフェースメンバを実装するクラス又は構造体内でインタ
フェースメンバの実装を探す処理は,インタフェース写像と呼ばれる。
あるクラスC又は構造体Cのインタフェース写像では,Cの基底クラスの並びに指定された各インタフ
ェースごとの各メンバの実装を探す。IがメンバMを宣言したインタフェースであるとき,特定のインタ
フェースメンバI.Mの実装は,まずCから連続したCの各基底クラスまで,一致するものが見つかるまで
各クラス又は構造体Sを検査することで決定される。
− もし,クラス又は構造体がI及びMに一致する明示的なインタフェースメンバの実装を含む場合,こ
のメンバをI.Mの実装とする。
− そうでないとき,クラス及び構造体が静的でない公開メンバMを含むのならば,このメンバをI.Mの
実装とする。
Cの基底クラスの並びに指定されたすべてのインタフェースのすべてのメンバについて実装が探し出せ
なければ,コンパイル時エラーになる。インタフェースのメンバは,基底インタフェースから継承したメ
ンバを含む。
構築インタフェース型のメンバの型仮引数も,25.5.4で規定するとおり,対応する型実引数で置き換え
られたものとする。
例 次に,総称インタフェース宣言の例を示す。
interface I<T>
{
T F(int x, T[,] y);
T this[int y] { get; }
}
この場合,構築インタフェース I<string[]>のメンバは,次のとおりである。
string[] F(int x, string[,][] y);
string[] this[int y] { get; }
インタフェース写像のために,クラスメンバAがインタフェースメンバBに一致するのは,次の場合と
する。
− A及びBがメソッドであり,AとBの名前,型及び仮引数の並びが同一である。
− A及びBが特性メンバであり,AとBの名前,型及び仮引数の並びが同一であり,AはBと同じアク
セス子をもつ(Aは,明示的なインタフェースメンバの実装でなければ,追加のアクセス子をもって
もよい)。
− A及びBがイベントであり,AとBの名前及び型が同一である。
− A及びBが添字子であり,AとBの型及び仮引数の並びが同一であり,AはBと同じアクセス子をも
つ(Aは,明示的なインタフェースメンバの実装でなければ,追加のアクセス子をもってもよい)。
インタフェース写像のアルゴリズムの主な特徴は,次のとおりである。
− インタフェースメンバを実装するクラス又は構造体のメンバを決定するときに,インタフェースメン
バの明示的実装は,同一のクラス又は構造体の他のメンバより高い優先順位をもつ。
− 非公開メンバや静的メンバは,インタフェース写像の対象にはならない。
例 次に例を示す。
391
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
interface ICloneable
{
object Clone();
}
class C: ICloneable
{
object ICloneable.Clone() {…}
public object Clone() {…}
}
CのICloneable.Cloneメンバは,ICloneableのCloneの実装になる。なぜなら,明示
的なインタフェースメンバの実装が他のメンバよりも優先されるからである。
クラス及び構造体が,同じ名前,型,及び仮引数型をもつメンバを含む二つ以上のインタフェースを実
装する場合は,これらのインタフェースメンバのそれぞれを,一つのクラス又は構造体メンバに対応付け
ることができる。
例 次に例を示す。
interface IControl
{
void Paint();
}
interface IForm
{
void Paint();
}
class Page: IControl, IForm
{
public void Paint() {…}
}
ここでは,IControlとIFormの両方のPaintメソッドは,Pageクラスの一つのPaintメ
ソッドに対応付けられる。もちろん,この二つのメソッドに対して,個別に明示的なインタフェ
ースメンバの実装をもつこともできる。
クラス及び構造体が,隠ぺいされたメンバをもつインタフェースを実装する場合,一部のメンバはイン
タフェースメンバの明示的実装を用いて実装しなければならない。
例 次に例を示す。
interface IBase
{
int P { get; }
}
interface IDerived: IBase
{
new int P();
392
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
このインスタンスの実装は,少なくとも一つのインタフェースメンバの明示的実装を必要とし,
次の形式のいずれかになる。
class C: IDerived
{
int IBase.P { get {…} }
int IDerived.P() {…}
}
class C: IDerived
{
public int P { get {…} }
int IDerived.P() {…}
}
class C: IDerived
{
int IBase.P { get {…} }
public int P() {…}
}
クラスが,同じ基底インタフェースをもつ複数のインタフェースを実装する場合,その基底インタフェ
ースの実装は一つだけ存在できる。
例 次に例を示す。
interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
interface IListBox: IControl
{
void SetItems(string[] items);
}
class ComboBox: IControl, ITextBox, IListBox
{
void IControl.Paint() {…}
void ITextBox.SetText(string text) {…}
void IListBox.SetItems(string[] items) {…}
}
基底クラスの並びに挙げられたIControlについて,ITextBoxから継承したIControlと,
393
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
IListBoxから継承したIControlという個別の実装をもつことはできない。実際,これらのイ
ンタフェースを個別に識別する概念はない。その代わり,ITextBox及びIListBoxはIControl
の同じ実装を共有し,ComboBoxは単純に三つのインタフェース,IControl,ITextBox及び
IListBoxを実装するとみなされる。
基底クラスのメンバは,インタフェース写像の対象となる。
例 次に例を示す。
interface Interface1
{
void F();
}
class Class1
{
public void F() {}
public void G() {}
}
class Class2: Class1, Interface1
{
new public void G() {}
}
Class1のFメソッドは,Class2におけるInterface1の実装として利用される。
20.4.3 インタフェース実装の継承
クラスは,その基底クラスによって提供されるすべてのインタフェース実装を継承する。
インタフェースを明示的に再実装しないと,派生クラスは,その基底クラスから継承したインタフェー
ス写像を変更できない。
例 次に例を示す。
interface IControl
{
void Paint();
}
class Control: IControl
{
public void Paint() {…}
}
class TextBox: Control
{
new public void Paint() {…}
}
TextBoxのPaintメソッドは,ControlのPaintメソッドを隠ぺいしている。しかし,
Control.PaintがIControl.Paintに対応付けされていることを変更するわけではない。ク
ラスインスタンスやインタフェースインスタンスを通してPaintを呼び出すと,次のような影響
394
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
がある。
Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();
// invokes Control.Paint();
t.Paint();
// invokes TextBox.Paint();
ic.Paint();
// invokes Control.Paint();
it.Paint();
// invokes Control.Paint();
しかし,インタフェースメソッドがクラス内の仮想メソッドに対応付けられる場合は,派生クラスが仮
想メソッドを上書きし,そのインタフェースの実装を変更できる。
例 例えば,先の例を次のように書き換える。
interface IControl
{
void Paint();
}
class Control: IControl
{
public virtual void Paint() {…}
}
class TextBox: Control
{
public override void Paint() {…}
}
この結果は次のようになる。
Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();
// invokes Control.Paint();
t.Paint();
// invokes TextBox.Paint();
ic.Paint();
// invokes Control.Paint();
it.Paint();
// invokes TextBox.Paint();
インタフェースメンバの明示的実装は仮想で宣言できないため,インタフェースメンバの明示的実装は
上書きできない。しかし,インタフェースメンバの明示的実装で別のメソッドを呼出し,派生クラスがそ
れを上書きするためにそのメソッドを仮想として宣言することは有効とする。
例 次に例を示す。
interface IControl
{
void Paint();
395
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
class Control: IControl
{
void IControl.Paint() { PaintControl(); }
protected virtual void PaintControl() {…}
}
class TextBox: Control
{
protected override void PaintControl() {…}
}
ここでは,Controlから派生したクラスが,PaintControlメソッドを上書きすることで,
IControl.Paintの実装を独自のものにすることができる。
20.4.4 インタフェースの再実装
インタフェースの実装を継承するクラスは,基底クラスの並びにそのインタフェースを含めることによ
って,インタフェースを再実装できる。
インタフェースの再実装は,インタフェースの初期実装と全く同じインタフェース写像規則に従う。こ
のため,継承されたインタフェース写像は,インタフェースの再実装で確立されるインタフェース写像に
影響を及ぼさない。
例 次に例を示す。
interface IControl
{
void Paint();
}
class Control: IControl
{
void IControl.Paint() {…}
}
class MyControl: Control, IControl
{
public void Paint() {}
}
ControlではIControl.PaintがControl.IControl.Paintに対応付けられていること
は,IControl.PaintがMyControl.Paintに対応付けられているMyControlでの再実装に
は影響を与えない。
継承された公開メンバの宣言と,継承されたインタフェースメンバの明示的宣言は,再実装されるイン
タフェースのインタフェース写像処理の対象になる。
例 次に例を示す。
interface IMethods
{
void F();
396
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void G();
void H();
void I();
}
class Base: IMethods
{
void IMethods.F() {}
void IMethods.G() {}
public void H() {}
public void I() {}
}
class Derived: Base, IMethods
{
public void F() {}
void IMethods.H() {}
}
ここでのDerivedのIMethodsの実装では,インタフェースのメソッドが,Derived.F,
Base.IMethods.G,Derived.IMethods.H及びBase.Iに対応付けられる。
クラスがインタフェースを実装する場合は,そのインタフェースのすべての基底インタフェースも暗黙
に実装する。同様に,インタフェースの再実装では,そのインタフェースのすべての基底インタフェース
も暗黙に再実装する。
例 次に例を示す。
interface IBase
{
void F();
}
interface IDerived: IBase
{
void G();
}
class C: IDerived
{
void IBase.F() {…}
void IDerived.G() {…}
}
class D: C, IDerived
{
public void F() {…}
public void G() {…}
}
397
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ここでは,IDerivedの再実装はIBaseも再実装し,IBase.FはD.Fに対応付けられる。
20.4.5 抽象クラス及びインタフェース
非抽象クラスと同様に,抽象クラスは,そのクラスの基底クラスの並びに含まれるインタフェースのす
べてのメンバの実装を提供しなければならない。ただし,抽象クラスは,インタフェースメソッドを抽象
メソッドに対応付けてもよい。
例 次に例を示す。
interface IMethods
{
void F();
void G();
}
abstract class C: IMethods
{
public abstract void F();
public abstract void G();
}
ここでのIMethodsの実装では,FとGが抽象メソッドに対応付けられる。これらは,Cから
派生する非抽象クラスの中で上書きされなければならない。
明示的なインタフェースメンバの実装は,抽象メンバにすることができない。しかし,明示的なインタ
フェースメンバの実装は,もちろん抽象メンバを呼び出すことができる。
例 次に例を示す。
interface IMethods
{
void F();
void G();
}
abstract class C: IMethods
{
void IMethods.F() { FF(); }
void IMethods.G() { GG(); }
protected abstract void FF();
protected abstract void GG();
}
ここでは,Cから派生した非抽象クラスは,FFとGGとを上書きしなければならない。これに
よって,IMethodsの実質的な実装が提供される。
21 enum(列挙)
列挙型 (enum type) は,名前を付けた一組の定数を宣言する,識別可能な型とする。
例 次に例を示す。
enum Color
398
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
Red,
Green,
Blue
}
これは,Red,Green及びBlueをメンバにもつColorという名前の列挙型である。
21.1 列挙宣言
列挙宣言では,新しい列挙型を宣言する。列挙宣言はキーワードenumで始まり,そのenumの名前,
アクセス可能性,基礎とする型及びメンバを定義する。
≪列挙宣言≫:
≪属性群≫opt ≪列挙修飾子≫opt enum ≪識別子≫ ≪列挙基底≫opt ≪列挙本
体≫ ;opt
≪列挙基底≫:
: ≪整数型≫
≪列挙本体≫:
{ ≪列挙メンバ宣言群≫opt }
{ ≪列挙メンバ宣言群≫ , }
各列挙型には,列挙型の基礎とする型と呼ばれる,対応する整数型がある。この基礎となる型は,列挙
で定義されるすべての列挙子値を表すことができなければならない。列挙宣言では,基礎とする型として
byte,sbyte,short,ushort,int,uint,long又はulongを明示的に宣言することができる。
注記 charは基礎となる型として利用できない。
基礎となる型を明示的に宣言しない列挙宣言は,基礎となる型としてint型をもつものとする。
例 次に例を示す。
enum Color: long
{
Red,
Green,
Blue
}
この例では,基礎となる型としてlongを伴って列挙を宣言している。
注記 この例のように開発者は,intの範囲ではなくlongの範囲の値を使えるようにするため,又
はこの選択肢を将来のために予約しておくために,基礎となる型としてlongを使うことがで
きる。
21.2 列挙修飾子
≪列挙宣言≫は,一つ以上の修飾子を含むことができる。
≪列挙修飾子群≫:
≪列挙修飾子≫
≪列挙修飾子群≫ ≪列挙修飾子≫
≪列挙修飾子≫:
new
399
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public
protected
internal
private
一つのenum宣言内で同じ修飾子を複数回使用すると,コンパイル時エラーになる。
列挙宣言の修飾子は,クラス宣言(17.1.1参照)のそれらと同じ意味をもつ。しかし,列挙宣言に
abstract修飾子及びsealed修飾子を使うことはできない。enumを抽象にすることはできず,派生も
できない。
21.3 列挙メンバ
列挙型の宣言の本体では,0個以上のメンバを定義する。これらのメンバは,列挙型の名前付き定数と
する。enumの二つのメンバに同じ名前を付けることはできない。
≪列挙メンバ宣言群≫:
≪列挙メンバ宣言≫
≪列挙メンバ宣言群≫ , ≪列挙メンバ宣言≫
≪列挙メンバ宣言≫:
≪属性群≫opt ≪識別子≫
≪属性群≫opt ≪識別子≫ = ≪定数式≫
enumの各メンバには,関連付けられた定数値がある。各メンバに関連付けられた値の型は,enumの基
礎となる型とする。enumの各メンバの定数値は,そのenumの基礎となる型の値域内にしなければなら
ない。
例 次に例を示す。
enum Color: uint
{
Red = -1,
Green = -2,
Blue = -3
}
この例は,コンパイル時エラーになる。なぜなら,定数値である-1,-2及び-3は,基礎とな
る整数型であるuintの範囲にないからである。
enumの複数のメンバで,関連付けられた同じ値を共有できる。
例 次に例を示す。
enum Color
{
Red,
Green,
Blue,
Max = Blue
}
この例では,BlueとMaxの二つの列挙メンバが,関連付けられた同じ値をもつことを表す。
400
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
列挙メンバに関連付けられた値は,暗黙的又は明示的に割り当てられる。enumのメンバの宣言に≪定
数式≫初期化子がある場合は,その定数式の値が(enumの基礎となる型に暗黙に変換され)enumのメン
バに関連付けられた値となる。enumのメンバの宣言に初期化子がない場合は,関連付けられた値が次の
とおりに暗黙に設定される。
− そのメンバが,列挙型で宣言された最初のメンバである場合,関連付けられた値は0になる。
− それ以外の場合,enumのメンバの関連付けられた値は,直前のメンバの関連付けられた値に1を加
えることで得られる。この値は,基礎となる型によって表すことができる値の範囲になければならな
い。
例 次に例を示す。
using System;
enum Color
{
Red,
Green = 10,
Blue
}
class Test
{
static void Main() {
Console.WriteLine(StringFromColor(Color.Red));
Console.WriteLine(StringFromColor(Color.Green));
Console.WriteLine(StringFromColor(Color.Blue));
}
static string StringFromColor(Color c) {
switch (c) {
case Color.Red:
return String.Format("Red = {0}", (int) c);
case Color.Green:
return String.Format("Green = {0}", (int) c);
case Color.Blue:
return String.Format("Blue = {0}", (int) c);
default:
return "Invalid color";
}
}
}
この例では,列挙メンバの名前,及び列挙メンバに関連付けられた値が出力される。出力は次
のようになる。
Red = 0
Green = 10
401
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Blue = 11
この理由は,次のとおりである。
・ 列挙メンバRedには,自動的にゼロが割り当てられる(これは初期化子がなく,最初のメンバ
であるため。)。
・ 列挙メンバGreenには,明示的に10が割り当てられる。
・ 列挙メンバBlueには,その前に記述されたメンバよりも1だけ大きい値 (11) が割り当てられ
る。
列挙メンバに関連付けられる値は,直接又は間接的に,自分自身に関連付けられたメンバの値を使用し
てはならない。この循環制限を除いて,列挙メンバの初期化子は,その記述位置にかかわらず,他のメン
バ初期化子を自由に参照できる。列挙メンバの初期化子内では,他のメンバの値が,基礎となる型をもつ
ものとして扱われる。このため,他のメンバを参照するときにキャストは不要である。
例 次に例を示す。
enum Circular
{
A = B,
B
}
この例では,AとBの宣言が循環しているので,コンパイル時エラーになる。Aは明示的にB
に依存しており,Bは暗黙にAに依存する。
列挙メンバの名前及び有効範囲は,クラス内のフィールドとほぼ同じ方法で設定される。列挙メンバの
有効範囲は,列挙型の本体になる。その有効範囲内では,列挙メンバを単純名で参照できる。他のコード
から参照する場合は,列挙メンバの名前をその列挙型の名前で限定しなければならない。列挙メンバは,
宣言されたアクセス可能性をもたない。列挙型にアクセス可能な場合は,そのメンバにもアクセス可能と
する。
21.4 System.Enum型
System.Enum型は,すべての列挙型の抽象基底クラスであり(これは,列挙型の基礎となる型とは明
確に異なる。),System.Enumから継承したメンバは,いずれの列挙型でも利用可能とする。ボックス化
変換(11.3.1参照)では,いずれの列挙型からもSystem.Enumへの変換が可能とする。ボックス化解除
(11.3.2参照)では,System.Enumからいずれの列挙型への変換も可能とする。
System.Enumそれ自身は,列挙型でない点に注意する必要がある。System.Enumは,すべての列挙
型が派生するクラス型とする。System.Enum型は,System.ValueType(11.1.1参照)から継承してお
り,そしてSystem.ValueTypeは,object型から継承している。実行時にSystem.Enum型の値は,
nullになることもあれば,いずれかの列挙型をボックス化した値への参照になることもある。
21.5 列挙値と演算
それぞれの列挙型は明確な型をもつ。列挙型と整数型との間の変換や,二つの列挙型の間の変換では,
明示的な列挙型の変換(13.2.2参照)を必要とする。列挙型が使用できる値の集合が,その列挙メンバに
よって制限されることはない。特に,enumの基礎となる型の値は,その列挙型にキャストでき,その列
挙型の固有の有効値となる。
列挙メンバは,そのメンバを囲む列挙型の型をもつ(ただし,他の列挙メンバ初期化子の中にある場合
を除く(21.3参照)。列挙型Eに宣言されている,関連付けられた値exprをもつ列挙メンバの値は,(E)v
402
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
とする。
次の演算子は,列挙型の値に使用できる。==,!=,<,>,<=,>=(14.9.5参照),+(14.7.4参照),-(14.7.5
参照),^,&,|(14.10.2参照),~(14.6.4参照),++,--(14.5.9及び14.6.5参照)及びsizeof(25.5.4
参照)。
すべての列挙型は,自動的にSystem.Enumクラスから派生する(そのEnumクラスは,更に
System.ValueType,そしてobjectから派生する。)。そのため,このクラスのメソッド及び特性メン
バは,列挙型の値に使用できる。
22 委譲
注記 委譲によって,他の言語において関数ポインタを扱うようなシナリオを可能にする。しかし,
メンバ関数へのポインタとは異なり,委譲は,オブジェクト指向であり,型安全である。委譲
は,オブジェクトインスタンス及びメソッドの両方をカプセル化する。
委譲宣言は,クラスSystem.Delegateから派生するクラスを定義する。委譲インスタンスは,一つ以
上のメソッドをカプセル化し,そのそれぞれを呼出し可能な実体と呼ぶ。インスタンスメソッドの場合,
呼出し可能な実体は,インスタンス及びそのインスタンスのメソッドで構成される。静的メソッドの場合,
呼出し可能な実体はメソッドだけで構成される。委譲インスタンスに適切な引数の集合を与えると,その
引数の集合を伴って,すべての委譲インスタンスのメソッドを呼び出すことができる。
注記 委譲インスタンスについて興味深く,有益な特徴は,カプセル化したメソッドのクラスについ
て委譲インスタンスが何も知らない点である。重要な点は,メソッドがその委譲型と一貫性
(22.1参照)があるかどうかだけである。これによって,委譲は“無名”呼出しに完全に適し
ている。
22.1 委譲宣言
委譲宣言は,新しい委譲型を宣言する型宣言(16.5参照)とする。
≪委譲宣言≫:
≪属性群≫opt ≪委譲修飾子群≫opt delegate ≪返却値型≫ ≪識別子≫ ≪型仮引数
並び≫opt ( ≪仮引数並び≫opt ) ≪型仮引数制約群節群≫opt ;
≪委譲修飾子群≫:
≪委譲修飾子≫
≪委譲修飾子群≫ ≪委譲修飾子≫
≪委譲修飾子≫:
new
public
protected
internal
private
一つの委譲宣言内で同じ修飾子を複数回使用すると,コンパイル時エラーになる。
委譲宣言は,≪型仮引数並び≫を提供することなしに,≪型仮引数制約群≫を提供してはならない。
≪型仮引数並び≫を提供する委譲宣言は,総称委譲宣言とする。
new修飾子は,他の型の中に宣言された委譲でだけ許される。その場合,その委譲は同じ名前で継承さ
れたメンバを隠ぺいすることを意味する(17.2.2.参照)。
403
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public,protected,internal及びprivateは,委譲型のアクセス可能性を制御する。委譲宣言
が行われる状況によっては,これらの修飾子の幾つかは使用できない(10.5.1参照)。
委譲の型名は≪識別子≫とする。
省略可能な≪仮引数並び≫は,委譲の仮引数を指定する。≪返却値≫は,委譲の返却値の型を示す。次
の条件を満たしている場合,メソッドと委譲の型には一貫性がある。
− メソッドのそれぞれの仮引数がref修飾子又はout修飾子をもたない場合,対応する委譲の仮引数
がref修飾子又はout修飾子をもたず,委譲の仮引数の型からメソッドの仮引数の型へ,恒等変換
又は暗黙の参照変換が可能である。また,メソッドのそれぞれの仮引数がref修飾子又はout修飾
子をもつ場合,対応する委譲の仮引数が同一の修飾子をもち,対応する委譲の仮引数の型が同一であ
る。
− メソッドの返却値の型から委譲型の返却値へ,恒等変換又は暗黙の参照変換が可能である。
この一貫性の定義によって,返却値の型の共変性と仮引数の型の共変性が可能になる。
C#における委譲型は,名前が同等でも,構造は同等ではないとする。特に,二つの委譲型において仮引
数の並びと返却値の型が同じであっても,異なる委譲型とみなす。
C#の委譲型は,名前が同じであれば同一のものであるが,構造が同じでも同一であるとは限らない。
注記 しかし,二つの全く別のものだが構造が同一の委譲型のインスタンスは,等価比較できる(14.9.8
参照)。
特に,同じ仮引数並び及び同じ返却値の型をもつ,二つの異なる委譲型は,別の委譲型であるとみなさ
れる。
例 次に例を示す。
delegate int D1(int i, double d);
class A
{
public static int M1(int a, double b) {…}
}
class B
{
delegate int D2(int c, double d);
public static int M1(int f, double g) {…}
public static void M2(int k, double l) {…}
public static int M3(int g) {…}
public static void M4(int g) {…}
delegate object D3(string s);
public static object M5(string s) {…}
public static int[] M6(object o) {…}
}
委譲型のD1及びD2は,同じ返却値の型及び同じ仮引数の並びをもつので,両方ともメソッド
A.M1及びB.M1と一貫性がある。委譲型のD1及びD2は,メソッドB.M2,B.M3及びB.M4と
は一貫性がない。メソッドB.M5及びB.M6は,両方とも委譲型D3と一貫性がある。
委譲型を宣言する唯一の方法は,≪委譲宣言≫を使用する。委譲型は,System.Delegateから派生す
404
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る参照型とする。委譲型は,暗黙に封印されているので,委譲型からはいかなる型も派生することが許さ
れない。また,System.Delegateから委譲クラス以外の型を派生することも許されない。
System.Delegate自身は,委譲型ではない。これは,すべての委譲型が派生するクラス型とする。
C#には,委譲の具現化及び呼出し用に,特定の構文が用意されている。具現化を除き,一般のクラス及
びクラスインスタンスに適用できる操作は,委譲クラス及び委譲インスタンスにも適用できる。特に,通
常のメンバアクセス構文を使って,System.Delegate型のメンバにアクセスできる。
委譲インスタンスによってカプセル化されたメソッドの集合を,呼出し並びと呼ぶ。一つの委譲インス
タンスを単一のメソッドから作成するとき(22.2参照)は,そのメソッドをカプセル化して,呼出し並び
には単一の入口だけを含める。しかし,二つのnullでない委譲インスタンスが組み合わされると,それ
らの呼出し並びは,左の演算対象から右の演算対象の順に連結される。それは,二つ以上の入口をもつ新
しい呼出し並びを構成する。呼出し並びが,空であることはできない。
委譲は,2項演算子+(14.7.4参照)及び+=(14.14.2参照)を使って組み合わせることができる。委譲
は,2項演算子-(14.7.5参照)及び−=(14.14.2参照)を使って,委譲の組合せから取り除くことができ
る。委譲は,等価比較を行うことができる(14.9.8参照)。
例 次の例は,幾つかの委譲の具現化と,それぞれに対応する呼出し並びを示している。
delegate void D(int x);
class Test
{
public static void M1(int i) {…}
public static void M2(int i) {…}
}
class Demo
{
static void Main() {
D cd1 = new D(Test.M1);
// M1
D cd2 = new D(Test.M2);
// M2
D cd3 = cd1 + cd2;
// M1 + M2
D cd4 = cd3 + cd1;
// M1 + M2 + M1
D cd5 = cd4 + cd3;
// M1 + M2 + M1 + M1 + M2
}
}
cd1及びcd2が具現化されると,それぞれは一つのメソッドをカプセル化する。cd3が具現化
されるとき,M1,M2の順番で二つのメソッドの呼出し並びをもつ。cd4の呼出し並びは,M1,
M2,M1の順番でメソッドを含む。最後に,cd5の呼出し並びは,M1,M2,M1,M1,M2の順番
でメソッドを含む。委譲の組合せ及び削除の詳細は,22.3を参照。
22.2 委譲の具現化
委譲のインスタンスは,≪委譲生成式≫によって生成される(14.5.10.3参照)。又は,無名メソッド及び
無名メソッドの集合から委譲型へ暗黙的な変換(13.5及び13.6参照)によって生成される。新しく生成さ
れたインスタンスは,次のいずれかを表す。新しく生成されたインスタンスは,次のいずれかを表す。
− 静的メソッド
405
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 対象オブジェクト(それはnullではあり得ない。)及びインスタンスメソッド
≪委譲生成式≫(14.5.10.3参照)の実引数が委譲インスタンスの場合,結果的に生成される委譲は,実
引数と同じ呼出し並びをもつものとする。その呼出し並びは,二つ以上の入口をもつかもしれない。
例 次に例を示す。
delegate void D(int x);
class Test
{
public static void M1(int i) {…}
public void M2(int i)
{…}
}
class Demo
{
static void Main() {
D cd1 = new D(Test.M1);
// static method
Test t = new Test();
D cd2 = new D(t.M2);
// instance method
D cd3 = new D(cd2);
// another delegate
}
}
委譲が具現化されると,委譲インスタンスは常に同じ対象オブジェクトの並び及びメソッドの並びを参
照する。
注記 二つの委譲が組み合わせられたり,一方から他方が取り除かれたりすると,固有の呼出し並び
をもつ新しい委譲が作成されるので注意する。組合せや削除の対象となった元の委譲の呼出し
並びは,変更されないままとする。
22.3 委譲による呼出し
C#には,委譲の呼出し用に特定の構文が用意されている。呼出し並びに一つの入口をもつ非null委譲
インスタンスが呼び出されると,呼び出された委譲インスタンスは,指定された同じ実引数を用いて一つ
のメソッドを呼出し,参照先のメソッドと同じ値を返す(14.5.5.2参照)。このような委譲の呼出し中に例
外が発生し,呼び出されたメソッド内でその例外が捕そくされない場合は,委譲が参照したメソッドをそ
のメソッドが直接呼び出したかのように,例外catch節の検索が委譲を呼び出したメソッド内で継続する。
複数の入口が格納された呼出し並びをもつ委譲インスタンスの呼出しは,その呼出し並びの各メソッド
を同期的に順に呼び出すことによって進行する。呼び出されたそれぞれのメソッドには,委譲インスタン
スに指定されたのと同じ実引数の集合が渡される。このような委譲の呼出しに参照仮引数(17.5.1.2参照)
が含まれる場合,各メソッド呼出しは,同じ変数への参照を用いて呼出しが行われる。すなわち,呼出し
並び内のあるメソッドがその変数に変更を加えると,呼出し並びのその後のメソッドでその変更が可視に
なる。委譲の呼出しに出力仮引数や返却値が含まれる場合,最終的な値は,並びの最後の委譲呼出しによ
って返される。このような委譲の呼出し処理中に例外が発生し,呼び出されたメソッド内でその例外が捕
そくされない場合,例外catch節の検索は委譲を呼び出したメソッド内で継続し,呼出し並び内のその後
のメソッドは呼び出さない。
nullを値としてもつ委譲インスタンスの呼出しを試みると,System.NullReferenceException型
406
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
の例外が発生する。
例 次の例は,委譲の具現化,組合せ,削除及び呼出しを示す。
using System;
delegate void D(int x);
class Test
{
public static void M1(int i) {
Console.WriteLine("Test.M1: " + i);
}
public static void M2(int i) {
Console.WriteLine("Test.M2: " + i);
}
public void M3(int i) {
Console.WriteLine("Test.M3: " + i);
}
}
class Demo
{
static void Main() {
D cd1 = new D(Test.M1);
cd1(-1);
// call M1
D cd2 = new D(Test.M2);
cd2(-2);
// call M2
D cd3 = cd1 + cd2;
cd3(10);
// call M1 then M2
cd3 += cd1;
cd3(20);
// call M1, M2, then M1
Test t = new Test();
D cd4 = new D(t.M3);
cd3 += cd4;
cd3(30);
// call M1, M2, M1, then M3
cd3 -= cd1;
// emove last M1
cd3(40);
// call M1, M2, then M3
cd3 -= cd4;
cd3(50);
// call M1 then M2
cd3 -= cd2;
cd3(60);
// call M1
cd3 -= cd2;
// impossible removal is benign
cd3(60);
// call M1
407
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
cd3 -= cd1;
// cd3is null
//
cd3(70);
// System.NullReferenceException
thrown
cd3 -= cd1;
// impossible removal is benign
}
}
上記の文cd3 += cd1が示すように,一つのメソッドは一つの呼出し並びに複数回出現できる。
この場合は,その委譲は,呼出し並びに出現する回数だけ,呼び出される。例のような呼出し並
びでそのメソッドが取り除かれる場合,実際に取り除かれるのは,呼出し並びで最後に出現する
メソッドである。
最後から一つ前の文,cd3 -= cd1の実行によって,委譲cd3はnullになる。nullから委
譲を差し引いても(又は,空でない並びから存在しない委譲を差し引いても),エラーにはならな
い。
生成される出力は次のとおりである。
Test.M1: -1
Test.M2: -2
Test.M1: 10
Test.M2: 10
Test.M1: 20
Test.M2: 20
Test.M1: 20
Test.M1: 30
Test.M2: 30
Test.M1: 30
Test.M3: 30
Test.M1: 40
Test.M2: 40
Test.M3: 40
Test.M1: 50
Test.M2: 50
Test.M1: 60
Test.M1: 60
23 例外
C#における例外は,システムレベル及びアプリケーションレベルの両方のエラー状態を扱うことができ
る構造化された,一様な型安全な方法を提供する。
注記 C#の例外処理機構は,C++のそれと同様とするが,次の幾つかの重要な点で違いがある。
・ C#では,すべての例外は,System.Exceptionクラスから派生したクラス型のインスタンス
で表現されなければならない。C++では,いずれの型のどんな値でも,例外を表すために使用
できる。
408
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
・ C#では,finallyブロック(15.10参照)が正常実行及び例外実行の両方で実行される終了コ
ードを記述するために使用される。C++では,これに相当するものはない。
・ C#の場合,けた(桁)あふれ,0による除算,空参照たぐりなどのシステムレベルの例外は,
適切に定義された例外クラスをもち,アプリケーションレベルのエラー状態と同等になる。
23.1 例外の原因
例外は,次の二つの方法で送出される。
− throw文は,すぐに無条件で例外を送出する。制御はthrow文(15.9.5参照)の直後に続く文には到
達しない。
− C#の文及び式の処理中に発生する特定の状態によって,処理を正常に終了できないときに,例外が発
生する。
例 例えば,整数除算(14.7.2参照)で分母がゼロの場合,System.DivideByZeroExceptionが
送出される。
このような方法で起こり得る様々な例外の一覧は,23.4を参照。
23.2 System.Exceptionクラス
System.Exceptionクラスは,すべての例外の基底型とする。このクラスは,すべての例外が共有す
る,幾つかの重要な特性メンバをもつ。
− Messageは,例外の理由を表す人間が可読な説明を含むstring(文字列)型の読込み専用の特性メ
ンバとする。
− InnerExceptionは,Exception型の読込み専用の特性メンバとする。もし,この値がnullでな
ければ,現在の例外を起こした原因となる例外を表す(すなわち,現在の例外は,InnerException
を処理するcatchブロックの中で発生したことになる。)。この値がnullの場合は,この例外が別の
例外によって起こされたものではないことを示す。この方法で連鎖した例外オブジェクトの個数は不
定とする。
これらの特性メンバの値は,System.Exceptionのインスタンス構築子を呼び出す際に指定できる。
注記 標準ライブラリは,System.Exceptionから直接継承する二つの型を提供する。すなわち,
System.SystemExceptionとSystem.ApplicationExceptionである。これらのクラス
は,それぞれ,システムによって定義された例外とアプリケーションによって定義された例外
とを区別する手段として提供される。それゆえ,System.Exceptionから直接派生するより
も,これらのクラスのうちの一つから適切に派生することが推奨される。
23.3 例外をどのように扱うか
例外は,try文(15.10参照)によって扱う。
例外が発生すると,システムは,例外を処理できる最も近いcatch節を探索する。このcatch節は,
例外の実行時型によって決まる。まず,字句的にtryブロックの閉じた箇所を見つけるために,現在のメ
ソッドの中が探索され,次にこのtry文に対応するcatch節が評価される。このcatch節が該当しない
場合,現在のメソッドを呼び出す部分を含んだ字句的に外側のtry文を探すために,現在のメソッドを呼
び出したメソッドが探索される。この探索は,送出された例外の実行時型と同一クラスである例外クラス
又は送出された例外の実行時型の基底クラスである例外クラスが指定された,現在の例外を処理できる
catch節が見つかるまで続けられる。例外の名前を指定していないcatch節は,任意の例外を処理でき
る。
一致するcatch節が見つかると,システムはそのcatch節の最初の文に制御が移行するように準備す
409
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る。catch節の実行を開始する前に,システムは,try文に関連付けられたすべてのfinally節を,そ
の例外を捕そくするところから,入れ子が深い順に実行する。
一致するcatch節が見つからない場合は,次のいずれかの処理が行われる。
− もし,一致するcatch節の探索が静的構築子(17.11参照)又は静的フィールド初期化子に到達する
ならば,静的構築子を呼び出す時点で,System.TypeInitializationExceptionが送出される。
System.TypeInitializationExceptionのInnerExceptionは,本来発生した例外を含む。
− 一致するcatch節の探索が,スレッドを最初に開始したコードに到達した場合は,スレッドの実行が
終了する。このような終了の影響は,実装で定義される。
終了化子で発生した例外が捕そくされない場合の振る舞いは,未規定である。
23.4 よくある例外クラス
次の例外が,幾つかのC#の操作によって送出される。
System.ArithmeticException
System.DivideByZeroExceptionや
System.OverflowExceptionなどの算術演算の際に発生
する例外の基底クラス。
System.ArrayTypeMismatchException
格納される要素の実行時の型が,配列の実行時の型と互換性
がないために,配列への格納に失敗した場合に送出される。
System.DivideByZeroException
整数値を0で除算しようとしたときに送出される。
System.IndexOutOfRangeException
0未満の添字,又は配列の範囲外の添字を用いて,配列の添
字指定をしようとしたときに送出される。
System.InvalidCastException
基底型又はインタフェースから派生型への明示的変換が,実
行時に失敗した場合に送出される。
System.NullReferenceException
参照先オブジェクトを必要とする状況でnull参照が使用さ
れた場合に送出される。
System.OutOfMemoryException
(newを使った)メモリの割当てが失敗した際に送出され
る。
System.OverflowException
検査文脈で算術演算がけた(桁)あふれした際に送出される。
System.StackOverflowException
保留状態のメソッド呼出しが多過ぎて,実行スタックに空き
がなくなったときに送出される。一般的には,再帰が深いか,
又は無限再帰の場合に,この例外が発生する。
System.TypeInitializationException
静的構築子が例外を送出し,その例外を捕そくするcatch
節が存在しない場合に送出される。
24 属性
注記 C#言語では,ほとんどの場合,プログラムで定義される実体についての宣言情報を指定できる。
例えば,クラス内のメソッドのアクセス可能性は,≪メソッド修飾子≫public,protected,
internal及びprivateを用いて指定する。
C#では,属性という,新しい種類の宣言情報を作成できる。属性は,様々なプログラム実体に添付でき
るし,実行時環境で属性情報を取り出すことができる。
注記 例えば,あるフレームワークでは,クラス及びメソッドといったプログラム実体について文書
化への対応付けをそのプログラム実体に使用できるHelpAttribute属性を定義するかもし
れない。
属性は,属性クラスの宣言(24.1参照)によって定義され,順序指定又は名前付きの仮引数(24.1.2参
照)をもってもよい。属性は,属性指定(24.3参照)を用いてC#プログラムの実体と関連付けられ,属性
410
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
インスタンス(24.2参照)として実行時に取り出すことができる。
24.1 属性クラス
System.Attribute抽象クラスから直接的又は間接的に派生したクラスを,属性クラスとする。属性
クラスの宣言は,プログラム実体に使用できる新しい種類の属性を定義する。規約によって,属性クラス
は,接尾辞Attributeを用いて名前を与える。属性の使用時には,この接尾辞を含めてもよいし省略で
きる。
総称クラス宣言は,直接又は間接のベースクラスとして,System.Attributeを使用できない。
例
using System;
public class B : Attribute {}
public class C<T> : B {}
// Error ‒ generic cannot be an attribute
24.1.1 属性の用法
AttributeUsage(24.4.1参照)属性は,属性クラスがどのように使用可能かを記述するために使用さ
れる。AttributeUsageは,属性クラスがそのクラスが使用可能な宣言の種類を指定できるようにする
ために,順序指定仮引数(24.1.2参照)をもつ。
例
using System;
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Interface)]
public class SimpleAttribute: Attribute
{}
この例では,SimpleAttributeという名前の属性クラスを定義する。この属性クラスは,
(AttributeUsageの指定から)≪クラス宣言≫及び≪インタフェース宣言≫だけに使用でき
る。さらに,次に例を示す。
[Simple] class Class1 {…}
[Simple] interface Interface1 {…}
これらは,Simple属性の使い方を示す。この属性は,SimpleAttributeという名前で定義されてい
るが,この属性が使われるとき,属性接尾辞は省略してもよく,その結果,短い名前Simpleとなってい
る。したがって,この例は,意味的に次と同じになる。
[SimpleAttribute] class Class1 {…}
[SimpleAttribute] interface Interface1 {…}
AttributeUsageは,AllowMultipleと呼ばれる名前付き仮引数(24.1.2参照)をもつ。それは,属
性が,与えられた実体に対して複数回指定可能かどうかを示す。属性クラスに対するAllowMultipleが
真の場合,そのクラスは,複数回使用属性クラスであって,実体に対して複数回指定できる。属性クラス
に対するAllowMultipleが偽又は未指定の場合,そのクラスは,一回使用属性クラスであって,実体に
対して高々1回だけ指定できる。
例
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AuthorAttribute: Attribute
411
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public AuthorAttribute(string name) {
this.name = name;
}
public string Name { get { return name;} }
private string name;
}
これは,複数回使用の属性クラスAuthorAttributeを定義する。次に使用例を示す。
[Author("Brian Kernighan"), Author("Dennis Ritchie")]
class Class1 {…}
この例は,Author属性を二つ使ったクラスの宣言を示す。AttributeUsageは,Inherited
と呼ばれる,他の名前付き仮引数(24.1.2参照)ももつが,この仮引数は,この属性が基底クラ
スに指定された場合,その基底クラスから派生されたクラスによっても継承されるかどうかを示
す。属性クラスに対するInheritedが偽の場合,その属性は継承されない。指定がない場合,
省略時の値は真とする。
次のように,AttributeUsage属性が添付されていない属性クラスXを考える。
using System;
class X: Attribute { … }
これは,次と同じになる。
using System;
[AttributeUsage(AttributeTargets.All, AllowMultiple = false,
Inherited = true)]
class X: Attribute { … }
24.1.2 順序指定仮引数及び名前付き仮引数
属性クラスは,順序指定仮引数及び名前付き仮引数をもつことができる。属性クラスの公開インスタン
ス構築子が,その属性クラスに対する,順序指定仮引数の有効な列を定義する。属性クラスに対する非静
的で公開の読み書き可能のフィールド及び特性が,その属性クラスの名前付き仮引数を定義する。特性の
アクセス子は,名前付き仮引数を定義するための特性に対して公開である必要がある。
例
using System;
[AttributeUsage(AttributeTargets.Class)]
public class HelpAttribute: Attribute
{
public HelpAttribute(string url) { // url is a positional parameter
…
}
public string Topic {
// Topic is a named parameter
get {…}
set {…}
}
412
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public string Url { get {…} }
}
この例は,一つの順序指定仮引数 (string url) 及び一つの名前付き仮引数 (string Topic)
をもつHelpAttributeという名前の属性クラスを定義する。この属性クラスは,非静的,かつ,
公開だが,Url特性は,読み書き可能ではないので,名前付き仮引数を定義しない。
この属性クラスは,次のとおりに使用できる。
[Help("http://www.mycompany.com/…/Class1.htm")]
class Class1
{
}
[Help("http://www.mycompany.com/…/Misc.htm", Topic ="Class2")]
class Class2
{
}
24.1.3 属性仮引数型
属性クラスの順序指定仮引数及び名前付き仮引数の型は,次に示す属性仮引数の型に制限される。
− 次のいずれか一つの型。bool,byte,char,double,float,int,long,short,及びstring。
− object型。
− System.Type型。
− 列挙型。ただし,それが,公開アクセス可能性をもち,それが入れ子にされている型がある場合には,
それらの型も公開アクセス可能性をもつものとする。
− 上記の型の1次元配列。
24.2 属性の指定
属性の指定とは,あらかじめ定義された属性をプログラム実体に適用する。属性は,宣言に指定される,
追加の宣言情報とする。属性は,(含む側の組立部品 (assembly) 属性を指定するために)大域的な有効
範囲で指定できる。プログラム実体は,≪型宣言≫(16.6参照),≪クラスメンバ宣言≫(17.2参照),≪
構造体メンバ宣言≫(18.2参照),≪インタフェースメンバ宣言≫(20.2参照),≪列挙メンバ宣言≫(21.1
参照),≪アクセス子宣言群≫(17.6.2参照),≪イベントアクセス子宣言群≫(17.7参照)及び≪仮引数
並び≫(17.5.1参照)の要素,≪型仮引数並び≫(25.1.1参照)の要素に対して指定できる。
属性は,属性節の中で指定される。属性節は,一つ以上の属性のコンマ区切り並びを囲む,1対の角括
弧で構成される。このような並びの中に指定される属性の順序,及び,同じプログラム実体に添付された
節の順序は,重要ではない。例えば,属性の指定,[A][B],[B][A],[A, B]及び[B, A]は,等しい。
≪大域的属性群≫:
≪大域的属性節群≫
≪大域的属性節群≫:
≪大域的属性節≫
≪大域的属性節群≫ ≪大域的属性節≫
≪大域的属性節≫:
[ ≪大域的属性指定定義≫ ≪属性並び≫ ]
[ ≪大域的属性指定定義≫ ≪属性並び≫ , ]
413
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪大域的属性指定定義≫:
≪大域的属性指定≫ :
≪大域的属性指定≫:
≪識別子≫
≪キーワード≫
≪属性群≫:
≪属性節群≫
≪属性節群≫:
≪属性節≫
≪属性節群≫ ≪属性節≫
≪属性節≫:
[ ≪属性指定定義≫opt ≪属性並び≫ ]
[ ≪属性指定定義≫opt ≪属性並び≫ , ]
≪属性指定定義≫:
≪属性指定≫ :
≪属性指定≫:
≪識別子≫
≪キーワード≫
≪属性並び≫:
≪属性≫
≪属性並び≫ , ≪属性≫
≪属性≫:
≪属性名≫ ≪属性実引数群≫opt
≪属性名≫:
≪型名≫
≪属性実引数群≫:
( ≪順序指定実引数並び≫opt )
( ≪順序指定実引数並び≫ , ≪名前付き実引数並び≫ )
( ≪名前付き実引数並び≫ )
≪順序指定実引数並び≫:
≪順序指定実引数≫
≪順序指定実引数並び≫ , ≪順序指定実引数≫
≪順序指定実引数≫:
≪属性実引数式≫
≪名前付き実引数並び≫:
≪名前付き実引数≫
≪名前付き実引数並び≫ , ≪名前付き実引数≫
≪名前付き実引数≫:
≪識別子≫ = ≪属性実引数式≫
≪属性実引数式≫:
414
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪式≫
属性は,≪属性名≫,並びに,省略可能な順序指定実引数及び名前付き実引数の並びで構成される。(存
在する場合,)順序指定実引数は,名前付き実引数より先にくる。順序指定実引数は,≪属性実引数式≫で
構成される。名前付き実引数は,名前,等号,≪属性実引数式≫の順序で構成される。これは,単純代入
と同じ規則によって制約される。名前付き実引数の順序は,重要ではない。
注記 C#は,≪大域的属性節≫及び≪属性節≫において,≪配列初期化子≫(19.6参照)と同様にコ
ンマが末尾に残る形式を許す。
≪属性名≫は,属性クラスを識別する。≪型名≫は,属性クラスを参照しなければならない。そうでな
いと,コンパイル時エラーになる。
例
class Class1 {}
[Class1] class Class2 {}
// Error
この例は,コンパイル時エラーになる。これは,Class1が属性クラスではないのに,Class1
を属性クラスとして使おうとしていることによる。
属性を大域的なレベルで設定する場合には,≪大域的属性指定定義≫が必要になる。≪大域的属性指定
≫としてはassemblyがある。この指定はアセンブリの文脈でだけ使用できる。
≪属性指定≫としてあらかじめ規定されているのは,event,field,method,param,property,
return,type及びtypevarである。これらの指定名は,次の文脈でだけ使用できる。
− event - イベント。
− field - フィールド。フィールドのように使用できるイベント(すなわち,アクセス子のないもの)
は,この属性指定名を伴う属性をもつことができる。
− method - 構築子,終了化子,メソッド,演算子,特性のget及びsetアクセス子,添字子のget
及びsetアクセス子,並びに,イベントのadd及びremoveアクセス子。フィールドのように使用
できるもの(すなわち,アクセス子のないもの)は,この属性指定名を伴う属性をもつことができる。
− param - 特性のsetアクセス子,添字子のsetアクセス子,イベントのadd及びremoveアクセ
ス子,構築子,メソッド並びに,演算子内の仮引数。
− property - 特性と添字子。
− return - 委譲,メソッド,演算子,特性のgetアクセス子,添字子のgetアクセス子。
− type - 委譲,クラス,構造体,列挙型,及びインタフェース。
− typevar - 型仮引数。
文脈によっては,複数の対象に対してある属性の指定を行うことができる。プログラムは,明示的に≪
属性指定定義≫を含めることによって対象を指定できる。≪属性指定定義≫の指定がない場合,適切な省
略時の値が適用されるが,≪属性指定定義≫を用いて,あいまいな場合の省略時の値を肯定又は上書きし
たり,あいまいでない場合でも省略時の値を単に肯定したりできる。このため,通常は≪属性指定定義≫
を省略できる。潜在的にあいまいな文脈は,次のとおりに解決される(9.4.2で定義されるものと等価なも
のを使用する。)。
− 委譲宣言で指定される属性は,宣言されている委譲又はその返却値のいずれかに対して適用できる。
≪属性指定定義≫が存在しない場合,属性は委譲に適用される。≪属性指定≫がtypeであると,属
性が委譲に対して適用されることを示す。≪属性指定≫がreturnであると,属性が返却値に対して
適用されることを示す。
415
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− メソッド宣言で指定される属性は,宣言されているメソッド又はその返却値のいずれかに適用できる。
≪属性指定定義≫が存在しない場合,属性はメソッドに適用される。≪属性指定≫がmethodである
と,属性がメソッドに適用されることを示す。≪属性指定≫がreturnであると,属性が返却値に適
用されることを示す。
− 演算子宣言で指定された属性は,宣言された演算子又はその返却値のいずれかに対して適用できる。
≪属性指定定義≫が存在しない場合,属性は,演算子に適用される。≪属性指定≫がmethodである
と,属性が演算子に適用されることを示す。≪属性指定≫がreturnであると,属性が返却値に適用
されることを示す。
− 特性宣言又は添字子宣言のgetアクセス子宣言で指定される属性は,関連付けられたメソッド又はそ
の返却値のいずれかに適用できる。≪属性指定定義≫が存在しない場合,属性は,メソッドに適用さ
れる。≪属性指定≫がmethodであると,属性がメソッドに適用されることを示す。≪属性指定≫が
returnであると,属性が返却値に適用されることを示す。
− 特性宣言又は添字子宣言のsetアクセス子で指定される属性は,関連付けられたメソッド又はその唯
一の暗黙の仮引数のいずれかに適用できる。≪属性指定定義≫が存在しない場合,属性は,メソッド
に適用される。≪属性指定≫がmethodであると,属性がメソッドに適用されることを示す。≪属性
指定≫がparamであると,属性が仮引数に適用されることを示す。
− イベントアクセス子宣言を省略するイベント宣言に指定される属性は,宣言されているイベント,関
連付けられたフィールド(イベントが抽象的でない場合),又は,関連付けられたaddメソッド及び
removeメソッドに適用できる。≪属性指定定義≫が存在しない場合,属性は,イベント宣言に適用
される。≪属性指定≫がeventであると,属性がイベントに適用されることを示す。≪属性指定≫が
fieldであると,属性がフィールドに適用されることを示す。≪属性指定≫がmethodであると,属
性がメソッドに適用されることを示す。
− イベントアクセス子宣言を行うイベント宣言のaddアクセス子宣言又はremoveアクセス子宣言で指
定される属性は,関連付けられたメソッド又はその唯一の仮引数に適用できる。≪属性指定定義≫が
存在しない場合,属性は,メソッドに適用される。≪属性指定≫がmethodであると,属性がメソッ
ドに適用されることを示す。≪属性指定≫がparamであると,属性が仮引数に適用されることを示す。
実装は,その目的を実装定義とする他の属性指定定義を受理してもよい。しかし,そのような指定対象
を認識しない実装は,警告を発行しなければならない。
規約によって,属性クラスは,Attribute接尾辞を用いて名前を付けられる。≪属性名≫は,この接
尾辞を含めてもよいし省略してもよい。
− ≪属性指定≫の最も右の識別子は,そのまま識別子である(9.4.2参照)。そして,≪属性名≫は,型
名として解決される。結果がSystem.Attributeから派生した型でないならばコンパイル時エラー
が発生する。
− そうでなければ
・ エラーは抑止され,≪属性名≫は型名として解決される。この解決が成功し,結果が
System.Attributeから派生した型に帰着したならば,型はこのステップの結果である。
・ ≪属性名≫の中の最も右の識別子に文字Attributeが追加される。トークンの結果となる文字列は型
名として解決され,エラーは抑止される。この解決が成功し,結果がSystem.Attributeから派
生した型に帰着する。
これらの二つのステップのいずれかによって結果がSystem.Attributeから派生した型に帰着し
416
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
たとき,その型が,≪属性名≫の結果型となる。そうでなければ,コンパイル時エラーが発生する。
注記 ≪属性名≫を解決しようとするとき,接尾辞なしの属性参照において,この接尾辞付き及び接
尾辞なしの両方が属性クラスとして見つかった場合は,この接尾辞なしの属性クラス参照は,
あいまいであって,コンパイル時エラーにならなければならない。≪属性名≫を構成する右端
の≪識別子≫が逐語的識別子(9.4.2参照)となるように≪属性名≫が記述されている場合,接
尾辞なしの属性だけが一致し,上のような接尾辞のあいまいさを解決できる。
例
using System;
[AttributeUsage(AttributeTargets.All)]
public class X: Attribute
{}
[AttributeUsage(AttributeTargets.All)]
public class XAttribute: Attribute
{}
[X]
// error: ambiguity
class Class1 {}
[XAttribute]
// refers to XAttribute
class Class2 {}
[@X]
// refers to X
class Class3 {}
[@XAttribute]
// refers to XAttribute
class Class4 {}
この例は,二つの属性クラスX及びXAttributeを示す。属性参照[X]はあいまいである。こ
れは,属性X又は属性Xattributeのいずれも参照できることによる。逐語的な識別子を使用
することによって,このようなまれな場合にも,正確な意図を指定できる。属性[XAttribute]
はあいまいではない。ただし,XAttributeAttributeという名前の属性クラスが存在する場
合には,あいまいになる。クラスXの宣言が削除された場合,次のとおりに,両方ともXattribute
という名前の属性クラスを参照する。
using System;
[AttributeUsage(AttributeTargets.All)]
public class XAttribute: Attribute
{}
[X]
// refers to XAttribute
class Class1 {}
[XAttribute]
// refers to XAttribute
class Class2 {}
[@X]
// error: no attribute named “X”
class Class3 {}
1回使用属性クラスを同じ実体で複数回使用すると,コンパイル時エラーになる。
例
417
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
[AttributeUsage(AttributeTargets.Class)]
public class HelpStringAttribute: Attribute
{
string value;
public HelpStringAttribute(string value) {
this.value = value;
}
public string Value { get {…} }
}
[HelpString("Description of Class1")]
[HelpString("Another description of Class1")]
public class Class1 {}
この例は,コンパイル時エラーになる。これは,1回使用属性クラスであるHelpStringを
Class1の宣言で複数回使用しようとしていることによる。
次の条件がすべて真となる場合,式Eは,≪属性実引数式≫とする。
− Eの型が,属性仮引数の型である(24.1.3参照)。
− コンパイル時に,Eの値が,次のいずれかの一つに解決できる。
・ 定数値。
・ 非総称型を指定するtypeof式 (14.5.11),閉構築型 (25.5.2),非束縛総称型 (25.2)
・ ≪属性実引数式≫の一次元配列。
例
using System;
[AttributeUsage(AttributeTargets.Class)]
public class MyAttribute: Attribute
{
public int P1 {
get {…}
set {…}
}
public Type P2 {
get {…}
set {…}
}
public object P3 {
get {…}
set {…}
}
}
[My(P1 = 1234, P3 = new int[]{1, 3, 5}, P2 = typeof(float))]
418
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class MyClass {}
class C<T> {
[My(P2 = typeof(T))]
// Error ‒ T not a closed type.
int x1;
[My(P2 = typeof(C<T>))]
// Error ‒ C<T> not a closed type.
int x2;
[My(P2 = typeof(C<int>))]
// Ok
int x3;
[My(P2 = typeof(C<>))]
// Ok
int x4;
}
複数の箇所で宣言された型の属性は,それぞれの部分で宣言された属性を結合することによっ
て決定する。結合順序は規定しない。同じ属性が複数の部分に置かれているならば,それはその
属性が型に対して複数回指定されたことと同じ意味をもつ。
例
[Attr1, Attr2("hello")]
partial class A {}
[Attr3, Attr2("goodbye")]
partial class A {}
これは,次の単一の宣言と同じ意味をもつ。
[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}
型仮引数の属性も同じ方法で結合する。
24.3 属性インスタンス
属性インスタンスは,実行時に属性を表現するインスタンスとする。属性には,属性クラス,順序指定
仮引数及び名前付き実引数が定義されている。属性インスタンスは,順序指定実引数及び名前付き実引数
を用いて初期化される属性クラスのインスタンスとする。
属性インスタンスを取り出すには,24.3.1〜24.3.2で示すとおり,コンパイル時処理及び実行時処理の両
方が関与する。
24.3.1 属性のコンパイル
次の手順で,属性クラスT,≪順序指定実引数並び≫P及び≪名前付き実引数並び≫Nをもつ,またプ
ログラム要素Eに指定される≪属性≫を,アセンブリAにコンパイルが行われる。
− new T(P)形式の≪オブジェクト生成式≫のコンパイルのためのコンパイル処理手順に従う。この手
順は,コンパイル時エラーになるか,又は,実行時に呼び出すことが可能なTのインスタンス構築子
を決定する。このインスタンス構築子をCと呼ぶ。
− Cが公開アクセス可能性をもっていない場合,コンパイル時エラーになる。−
Nの中のそれぞれの
≪名前付き実引数≫Argに対して,次を行う。
・ Nameを,≪名前付き実引数≫Argの≪識別子≫とする。
419
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
・ Nameは,Tの非静的な読み書き可能な公開のフィールド又は特性として識別されなければならない。
Tがフィールド又は特性をもたない場合,コンパイル時エラーになる。
・ 属性の実行時インスタンス化のために,属性を含むプログラムを翻訳した結果としてコンパイラの
アセンブリ出力結果の中に,属性クラスT,Tのインスタンス構築子C,≪順序指定実引数並び≫P
及び≪名前付き実引数並び≫Nの情報,関連したプログラム要素Eを,コンパイル時に完全に解決
した値とともに格納する。
24.3.2 属性インスタンスの実行時取出し
属性クラスT,Tのインスタンス構築子C,≪順序指定実引数並び≫P及び≪名前付き実引数並び≫Nに
よって表現される,また,Eへの関連される属性インスタンスは,アセンブリAから実行時に取り出すこ
とができる。この情報に基づき,次の手順を用いて取り出せる。
− コンパイル時に決定されるとおりのインスタンス構築子Cを用いて,形式new T(P)の≪オブジェク
ト生成式≫を実行するための実行時処理手順に従う。この手順は,例外を生じるか,又はTのインス
タンスを生成する。このインスタンスをOと呼ぶ。
− N内のそれぞれの≪名前付き実引数≫ Argに対して,順に,次を実行する。
・ Nameを,≪名前付き実引数≫ Argの≪識別子≫とする。NameがOの非静的な読み書き可能のフ
ィールド又は特性として識別されない場合,例外が送出される。
・ Valueを,Argの≪属性実引数式≫を評価した結果とする。
・ NameがOのフィールドとして識別された場合,このフィールドに値Valueを設定する。
・ そうでない場合,Nameは,Oの特性として識別される。この特性に値Valueを設定する。
・ 結果は,O,すなわち,≪順序指定実引数並び≫ P及び≪名前付き実引数並び≫ Nを用いて初期化
された属性クラスTのインスタンスになる。
注記 Aの中で,T,C,P,N(それらとEと関連付けられたもの)の文字に対する形式と,Eを指定
する機構及びAからT,C,P,Nを取り出す機構(とひいては,どのように属性インスタンス
が実行時に取り出されるか)は,この標準の範囲外である。
例 CLIの実装では,24.1.2の例プログラムを翻訳することによって生成されたアセンブリ内のHelp
属性インスタンスは次のプログラムによって取り出される。
using System;
using System.Reflection;
public sealed class InterrogateHelpUrls
{
public static void Main(string[] args) {
Type helpType = typeof(HelpAttribute);
string assemblyName = args[0];
foreach (Type t in Assembly.Load(assemblyName).GetTypes()) {
Console.WriteLine("Type : {0}", t.ToString());
HelpAttribute[] helpers =
(HelpAttribute[])t.GetCustomAttributes(helpType, false);
for (int at = 0; at != helpers.Length; at++) {
420
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine("¥tUrl : {0}", helpers[at].Url);
}
}
}
}
24.4 予約された属性
次の属性が,C#言語に記述されるように影響する。
− System.AttributeUsageAttribute(24.4.1参照)。これは,属性クラスが使用可能な方法を記述
するために用いる。
− System.Diagnostics.ConditionalAttribute(24.4.2参照)は複数使用属性クラスである。こ
れは,条件付きメソッドと条件付き属性クラスを定義するために用いる。この属性は,属性付き翻訳
シンボルを試行することによって条件を示す。
− System.ObsoleteAttribute(24.4.3参照)。これは,メンバを互換性のためだけに残されている
と印を付けるために用いる。
24.4.1 AttributeUsage属性
属性AttributeUsageは,属性クラスが使用可能な方法を記述するために用いる。
AttributeUsage属性を用いて修飾されたクラスは,直接的又は間接的にSystem.Attributeから
派生されなければならない。それ以外の場合は,コンパイル時エラーになる。
注記 この属性の使用についての例は,24.1.1を参照。
24.4.2 Conditional属性
属性Conditionalは,条件付きメソッドと条件付き属性クラスの定義を可能にする。
24.4.2.1 Conditional属性
Conditional属性によって修飾されたメソッドは,条件付きメソッドである。各条件付きメソッドは,
そのConditional属性で宣言された条件翻訳シンボルと関連する。
using System.Diagnostics;
class Eg
{
[Conditional("ALPHA")]
[Conditional("BETA")]
public static void M() {
//...
}
}
上記は,条件翻訳シンボルALPHAとBETAに関連した条件付きメソッドとしてEq.Mを宣言する。
条件付きメソッドの呼出しは,一つ以上の関連した条件翻訳シンボルが,呼出しが行われる時点でこの
条件付きコンパイル記号が定義されているかどうかに依存して,条件付きメソッドへの呼出しが含まれる
か又は無視されるかのいずれかになる。記号が定義されている場合,呼出しが含まれ,定義されていない
場合,呼出しは無視される。
421
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
条件付きメソッドは,次の制限に従う。
− 条件付きメソッドは,≪クラス宣言≫又は≪構造体宣言≫の中のメソッドでなければならない。
Conditional属性がインタフェースメソッドに指定された場合,コンパイル時エラーになる。
− 条件付きメソッドは,返却型voidをもたなければならない。
− 条件付きメソッドは,override修飾子で印付けられてはならない。しかし,条件付きメソッドは,
virtual修飾子で印付けられてもよい。このようなメソッドの上書きは,暗黙に条件付きなので,明
示的にConditional属性を設定してはならない。
− 条件付きメソッドは,インタフェースメソッドの実装であってはならない。さもないと,コンパイル
時エラーになる。
さらに,条件付きメソッドを≪委譲生成式≫で使用した場合も,コンパイル時エラーになる。
例
#define DEBUG
using System;
using System.Diagnostics;
class Class1
{
[Conditional("DEBUG")]
public static void M() {
Console.WriteLine("Executed Class1.M");
}
}
class Class2
{
public static void Test() {
Class1.M();
}
}
この例は,条件付きメソッドとしてClass1.Mを宣言している。Class2のTestメソッドが,
このメソッドを呼び出す。条件付きコンパイル記号DEBUGは定義されているので,Class2.Test
が呼び出された場合,それはMを呼び出す。記号DEBUGが定義されていない場合,Class2.Test
は,Class1.Mを呼び出さない。
条件付きメソッドの呼出しが含まれるか含まれないかは,呼出し時点での条件付きコンパイル記号によ
って制御されるということを理解することが重要である。
例
// Begin class1.cs
using System;
using System.Diagnostics;
class Class1
{
[Conditional("DEBUG")]
422
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public static void F() {
Console.WriteLine("Executed Class1.F");
}
}
// End class1.cs
// Begin class2.cs
#define DEBUG
class Class2
{
public static void G() {
Class1.F();
// F is called
}
}
// End class2.cs
// Begin class3.cs
#undef DEBUG
class Class3
{
public static void H() {
Class1.F();
// F is not called
}
}
// End class3.cs
この例では,クラスClass2及びClass3は,それぞれ,DEBUGが定義されるかどうかに基づ
いて条件付けられた条件付きメソッドClass1.Fの呼出しを含む。
継承連鎖の中で条件付きメソッドを使用すると,混乱する可能性がある。形式base.Mの,baseを経
由した条件付きメソッドへの呼出しは,通常の条件付きメソッド呼出し規則に従う。
例
// Begin class1.cs
using System;
using System.Diagnostics;
class Class1
{
[Conditional("DEBUG")]
public virtual void M() {
Console.WriteLine("Class1.M executed");
}
}
423
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
// End class1.cs
// Begin class2.cs
using System;
class Class2: Class1
{
public override void M() {
Console.WriteLine("Class2.M executed");
base.M();
// base.M is not called!
}
}
// End class2.cs
// Begin class3.cs
#define DEBUG
using System;
class Class3
{
public static void Test() {
Class2 c = new Class2();
c.M();
// M is called
}
}
// End class3.cs
Class2は,その基底クラスの中で定義されるMの呼出しを含む。この基底メソッドは,記号
DEBUGの存在に依存する条件付きメソッドだが,この記号は未定義なので,この呼出しは省略さ
れる。そのためにそのメソッドは,“Class2.Mexecuted”とだけディスプレイ装置に出力する。
≪前処理宣言≫を適切に使うと,このような問題を回避できる。
24.4.2.2 条件付き属性クラス
一つ以上のConditional属性で修飾された属性クラス(24.1)は,条件付き属性クラスである。条件付
き属性クラスはConditional属性の中で宣言された条件翻訳シンボルと関連する。
例
using System;
using System.Diagnostics;
[Conditional("ALPHA")]
[Conditional("BETA")]
public class TestAttribute : Attribute {}
上記は,TestAttributeをALPHAとBETA条件翻訳シンボルに関連した条件付き属性クラス
として宣言する。
条件付き属性の属性指定(24.2参照)は,一つ以上の関連した条件翻訳シンボルが,指定が行われる時
424
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
点でこの条件付きコンパイル記号が定義されているかどうかに依存して,指定が含まれるか又は無視され
るかのいずれかになる。記号が定義されている場合,指定が含まれ,定義されていない場合,指定は無視
される。
条件付き属性クラスの属性指定が含まれるか・排除されるかは,その指定の時点での条件付き翻訳シン
ボルによって制御されることに注意することが重要である。
例
using System;
using System.Diagnostics;
[Conditional(“DEBUG”)]
public class TestAttribute : Attribute {}
File class1.cs:
#define DEBUG
[Test]
// TestAttribute is specified
class Class1 {}
File class2.cs:
#undef DEBUG
[Test]
// TestAttribute is not specified
class Class2 {}
クラスClass1及びClass2クラスはそれぞれ属性Testによって修飾されている。それは,
DEBUGが定義されているかどうかによって条件付けられている。なぜなら,このシンボルは
Class2ではなく,Class1の文脈内で定義されている,Class2のTest属性の指定が省略され
る一方で,Class1のTest属性の指定には含まれる。
24.4.3 Obsolete属性
Obsolete属性は,もはや使用しないことが望ましい型及び型のメンバに印を付けるために用いる。
プログラムがObsolete属性をもつ型又はメンバを使う場合,コンパイラは,開発者に警告するために
警告又はエラーを発行しなければならない。これによって,問題のあるコードを修正できる。特に,エラ
ー仮引数が提供されない場合,又はエラー仮引数は提供されても,その値が偽の場合,コンパイラは,警
告を発行しなければならない。コンパイラは,エラー仮引数が指定され,その値が真の場合,コンパイル
時エラーにならなければならない。
例
[Obsolete("This class is obsolete; use class B instead")]
class A
{
public void F() {}
}
class B
{
public void F() {}
}
425
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class Test
{
static void Main() {
A a = new A(); // warning
a.F();
}
}
クラスAは,Obsolete属性で修飾されている。Mainの中でのAのそれぞれの使用は,指定
されたメッセージ,“This class is obsolete; use class B instead(このクラスは,使用しないほうがよ
い。クラスBを代わりに使用すること)”を含む警告を生じる。
25 総称性
25.1 総称クラス宣言
総称クラス宣言は,実行時型を形成するために与えられる型仮引数を要求するクラスの宣言とする。
注記 クラス宣言(17.1参照)は,型仮引数及びそれに関連する制約を定義できる。
≪クラス宣言≫:
≪属性群≫opt
≪クラス修飾子≫opt
partialopt
class ≪識別子
≫ ≪型仮引数並び≫opt
≪クラス基底≫opt ≪型仮引数制約群節群≫opt ≪クラス本体≫ ; opt
≪クラス宣言≫は,≪型仮引数並び≫(25.1.1参照)を含まない場合には,≪型仮引数制約
群節群≫(25.7参照)を含んではならない。
≪型仮引数並び≫を含むクラス宣言が,総称クラス宣言になる。
総称クラス宣言又は総称構造型宣言(25.2参照)の中で入れ子になっているあらゆるクラスは,それ自
体が総称クラス宣言になる。これは,包含する型の型仮引数が,内部で構築型を生成するために提供され
なければならないことによる。
総称クラス宣言は,特に規定する場合を除き,総称でないクラス宣言と同じ規則に従う。総称クラス宣
言は,総称でないクラス宣言の中で入れ子にできる。
総称クラスは,構築型を用いて参照される(25.5参照)。
例 次の総称クラス宣言を考える。
class List<T> {}
構築型の例としては,List<T>,List<int>及びList<List<string>>がある。
List<T>などの一つ以上の型仮引数をもつ構築型を開構築型(25.5参照)という。List<int>などの
型仮引数をもたない構築型を閉構築型(25.5参照)という。
総称型は,型仮引数の個数に関して“多重定義”ができる。すなわち,同じ名前空間又は外部型宣言の
中の二つの型宣言は,型仮引数の個数が異なっている限り,同じ識別子を使用できる。
例
class C {}
class C<V> {}
// OK
struct C<U,V> {}
// OK
426
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class C<A,B> {}
// Error, C with two type parameters defined
twice
型名前解決(10.8参照),単純名前解決(14.5.2参照)及びメンバアクセス(14.5.4参照)において用い
られる型検索規則は,型仮引数の個数を重視する。
総称クラス宣言の基底インタフェースは,25.3.1で示される一意性規則を満たさなければならない。
25.1.1 型仮引数
型仮引数は,クラス宣言において与えることができる。各型仮引数は,構築型を生成するために与える
型実引数のための場所取りを表す単なる識別子とする。型仮引数は,後で与えられる型の形式的な場所取
りとする。これに対して,型実引数(25.5.1参照)は,構築型を生成するときに,型仮引数に代入される
実行時の型とする。
≪型仮引数並び≫:
< ≪型仮引数群≫ >
≪型仮引数群≫:
≪属性群≫opt ≪型仮引数≫
≪型仮引数群≫ , ≪属性群≫opt ≪型仮引数≫
≪型仮引数≫:
≪識別子≫
クラス宣言における各仮引数は,そのクラスの宣言空間(10.3参照)の中の名前を定義する。そのため
に,そのクラスで宣言された他の型仮引数又はメンバと同じ名前をもつことはできない。型仮引数は,そ
の型自体と同じ名前をもってはならない。
クラスにおける型仮引数の有効範囲(10.7参照)は,≪クラス基底≫,≪型仮引数制約群節群≫及び≪
クラス本体≫を含む。クラスのメンバとは異なり,この有効範囲は,派生クラスにまで拡張されることは
ない。型仮引数は,その有効範囲内で型として使用できる。
一つの型仮引数に多くの異なる実行時の型実引数を代入できるので,型仮引数には,他の型とは少し異
なる操作及び制限がある。
注記 これらの操作及び制限には,次を含む。
− 型仮引数は,基底クラス又はインタフェースを宣言するために直接には使えない(17.1.2参照)。
注記 ただし,制約(25.7参照)として型仮引数を使用し,間接的に,基底クラス又はインタフ
ェースの関係を定義することはできる。例えば,class C<A, B> where A : B として
よい。
− 型仮引数のメンバ検索の規則は,その型仮引数に適用される制約が存在する場合には,その制約
に依存する。これは,25.7.2に詳細に示す。
− 型仮引数に利用可能な変換は,その型仮引数に適用される制約が存在する場合には,その制約に
依存する。これは,25.7.4に詳細に示す。
− リテラルnullは,型仮引数が参照型だと分かる場合を除いて,型仮引数が与える型の値に
変換できない(25.7.4参照)。しかし,型仮引数の省略時の値を生成するために,省略時値式(14.5.14
参照)を使用できる。
− 型仮引数に対するnew式(14.5.10.1参照),例えば new T() は,型仮引数が≪構築子制約≫又
は値型制約によって制約される場合だけに,使用できる(25.7参照)。
− 型仮引数は,属性内のどこでも使用できない(24.2参照)。
427
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 型仮引数は,静的メンバ(14.5.4参照)を識別するためにメンバアクセスでは使用できないし,
入れ子型(10.8参照)を識別するために型名でも使用できない。
− 安全ではないコードでは,型仮引数は,≪非管理型≫として使用できない(27.2参照)。
型としては,型仮引数は,コンパイル時に構築される型とする。実行時に,各型仮引数は,総称型宣言
に型実引数を与えることによって指定される実行時型と結合される。このように,型仮引数をもつとして
宣言された変数の型は,実行時に閉構築型になる(25.5.2参照)。型仮引数を含むすべての文及び式の実行
時における実行は,その型仮引数に対して型実引数として与えた実行時型を使用する。
25.1.2 インスタンス型
各クラス宣言は,関連する構築型,すなわちインスタンス型をもつ。総称クラス宣言に対しては,供給
された型実引数のそれぞれを対応する型仮引数に与えて,その型宣言から構築型(25.5参照)を生成する
ことによって,インスタンス型として使えるようになる。そのインスタンス型は,型仮引数を使用するの
で,型仮引数がその有効範囲内にあるところだけで,すなわちそのクラス宣言の中だけで,使用できる。
インスタンス型は,クラス宣言内で書かれるコードに対するthisの型とする。総称でないクラスに対し
ては,インスタンス型は,単に,その宣言されたクラスとする。
例 次に,幾つかのクラス宣言を,そのインスタンス型とともに示す。
class A<T>
// instance type: A<T>
{
class B {}
// instance type: A<T>.B
class C<U> {}
// instance type: A<T>.C<U>
}
class D {}
// instance type: D
25.1.3 総称クラスのメンバ
総称クラスのすべてのメンバは,直接に又は構築型の一部として,あらゆる取り囲むクラスからの型仮
引数を使用できる。特定の閉構築型(25.5.2参照)が実行時に使用されている場合,それぞれで使用され
ている型仮引数は,その構築型に与えられている実行時の型実引数で置き換えられる。
例 次に,幾つかのクラス宣言を,そのインスタンス型とともに示す。
class C<V>
{
public V F1;
public C<V> F2 = null;
public C(V x) {
this.F1 = x;
this.F2 = this;
}
}
class Application
{
static void Main() {
C<int> x1 = new C<int>(1);
428
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine(x1.F1);
// Prints 1
C<double> x2 = new C<double>(3.1415);
Console.WriteLine(x2.F1);
// Prints 3.1415
}
}
インスタンス関数メンバ内では,thisの型は,包含する宣言のインスタンス型(25.1.2参照)とする。
型としての型仮引数を使う場合以外は,総称クラス宣言におけるメンバは,総称でないクラスのメンバ
と同じ規則に従う。特定の種類のメンバに適用される追加規則は,25.1.4〜25.1.11で示す。
25.1.4 総称クラスにおける静的フィールド
総称クラス宣言における静的変数は,同じ閉構築型(25.5.2参照)のすべてのインスタンス間で共有さ
れるが,異なる閉構築型のインスタンス間では共有されない。この規則は,静的変数の型が型仮引数を含
むかどうかに関係なく,適用される。
例
class C<V>
{
static int count = 0;
public C() {
count++;
}
public static int Count {
get { return count; }
}
}
class Application
{
static void Main() {
C<int> x1 = new C<int>();
Console.WriteLine(C<int>.Count);
// Prints 1
C<double> x2 = new C<double>();
Console.WriteLine(C<double>.Count);
// Prints 1
Console.WriteLine(C<int>.Count);
// Prints 1
C<int> x3 = new C<int>();
Console.WriteLine(C<int>.Count);
// Prints 2
}
}
25.1.5 総称クラスにおける静的構築子
総称クラスにおける静的構築子は,静的フィールドの初期化と,その総称クラス宣言から生成されるそ
れぞれの異なる閉構築型に対するそれ以外の初期化とを実行するために使用する。総称型宣言の型仮引数
は,その静的構築子の本体内にあれば,有効範囲内にあり,使用できる。
429
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
新しい閉構築クラス型は,次のいずれかの最初の時点で,初期化される。
− 閉構築型のインスタンスが生成される。
− 閉構築型の静的メンバのいずれかが参照される。
新しい閉構築クラス型を初期化するためには,まず,その特定の閉構築型に対する静的フィールド(25.1.4
参照)の新しい集合を生成する。その静的フィールドは,それぞれ,その省略時値に初期化される(12.2
参照)。次に,静的フィールド初期化子(17.4.5.1参照)が,静的フィールドに対して実行される。最後に,
静的構築子が実行される。
静的構築子はそれぞれの閉構築型に対して正確に一回実行されるので,制約(25.7参照)を通じたコン
パイル時には検査できない型仮引数に関する実行時検査を強制実行するには,この時点がよい。
例 次の型は,型実引数をenum型とするために静的構築子を使っている。
class Gen<T> where T: struct
{
static Gen() {
if (!typeof(T).IsEnum) {
throw new ArgumentException("T must be an enum");
}
}
}
25.1.6 限定公開メンバへのアクセス
注記 総称クラスで宣言された限定公開メンバのアクセス可能領域(10.5.2参照)は,その総称クラ
スから構築されたあらゆる型から派生するすべてのクラス宣言のプログラムテキストを含む。
例えば,次のとおり。
class C<T>
{
protected static T x;
}
class D: C<string>
{
static void Main() {
C<int>.x = 5;
}
}
D内の限定公開メンバC<int>.xへの参照は,クラスDがC<string>から派生していても,有効にな
っている。
総称クラス宣言内では,継承された限定公開のインスタンスメンバ(10.5.3参照)へのアクセスは,そ
の総称クラスから構築されたあらゆるクラス型のインスタンスを通じて許可される。
例 次のコードを考える。
class C<T>
{
protected T x;
430
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
class D<T>: C<T>
{
static void F() {
D<T> dt = new D<T>();
D<int> di = new D<int>();
D<string> ds = new D<string>();
dt.x = default(T);
di.x = 123;
ds.x = "test";
}
}
xへの三つの代入は,すべて,総称クラスから構築されたクラス型のインスタンスを通じて行
われているので,許可される。
25.1.7 総称クラスにおける多重定義
総称クラス宣言内のメソッド,構築子,添字子及び演算子は,多重定義できる。宣言された際の呼出し
情報は一意でなければならないが,型実引数の代入によって同じ呼出し情報が生じることがある。その場
合,最も限定的なものを選択して,多重定義を解決しようとする(14.4.2.2参照)。
注記 最後の文は,規定として明確ではなく,次のとおりに解釈するのがよい。
その場合,型実引数の代入の前に,元の呼出し情報の最も限定的なものを選択しようとし,
それが存在するときには,多重定義を解決する (14.4.2.2)。存在しない場合には,エラーを報告
する。
例 次の例は,この規則に従って有効である多重定義を示している。
注記 この説明は,正確ではなく,次のとおりに考えるのがよい。
次の例は,この規則に従った有効性及びあいまい性を示している。すべての呼出し情報は有
効だが,代入によってはあいまい性が生じる。
interface I1<T> {…}
interface I2<T> {…}
class G1<U>
{
long F1(U u) {…}
// Overload resolution for G<int>.F1
int F1(int i) {…}
// will pick non-generic
void F3(I1<U> a) {…} // Valid overload
void F3(I2<U> a) {…}
}
class G2<U, V>
{
void F5(U u, V v) {…} // Overload resolution for G2<int, int> would
void F5(V v, U u) {…} // fail
void F6(U u, I1<V> v) {…} // Valid, overload resolution on G2<I1<int>,
431
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int>
// is ambiguous
void F6(I1<V> v, U u) {…}
void F7(U u1, I1<V> v2) {…} // Valid overload
void F7(V v1, U u2) {…}
void F8(ref U u) {…}
// Valid overload
void F8(out V v) {…}
}
25.1.8 仮引数配列をもつメソッド及び型仮引数
型仮引数は,仮引数配列の型に使用できる。
例 次の宣言を考える。
class C<V>
{
static void F(int x, int y, params V[] args) { … }
}
そして,次のメソッドの呼出しを考える。
C<int>.F(10, 20);
C<object>.F(10, 20, 30, 40);
C<string>.F(10, 20, "hello", "goodbye");
これらの展開した形式は,次になる。
C<int>.F(10, 20, new int[] {});
C<object>.F(10, 20, new object[] {30, 40});
C<string>.F(10, 20, new string[] {"hello", "goodbye"} );
25.1.9 上書き及び総称クラス
総称クラスにおける関数メンバは,総称でないクラスと同じように,基底クラスにおける関数メンバを
上書きできる。上書きされた基底クラスのメンバ決定は,25.5.4で示すとおりに,型実引数を代入するこ
とによって行う。基底クラスのメンバが決定されたら,上書きの規則は,総称でないクラスに対する規則
と同じとする。
例 次は,上書き規則が,総称性がある場合にどう機能するかを示す。
abstract class C<T>
{
public virtual T F() {…}
public virtual C<T> G() {…}
public virtual void H(C<T> x) {…}
}
class D: C<string>
{
public override string F() {…}
// Ok
public override C<string> G() {…}
// Ok
public override void H(C<int> x) {…}
// Error, should be
432
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
C<string>
}
class E<T, U>: C<U>
{
public override U F() {…}
// Ok
public override C<U> G() {…}
// Ok
public override void H(C<T> x) {…}
// Error, should be C<U>
}
25.1.10
総称クラスにおける演算子
総称クラス宣言は,総称でないクラス宣言と同じ規則に従って,演算子を定義できる。クラス宣言のイ
ンスタンス型(25.1.2参照)は,次に示すとおり,演算子に対する通常の規則に類似した方法で,演算子
の宣言において使用できる。
− 単項演算子は,その単項演算子を定義するインスタンス型の仮引数を一つ取らなければならない。単
項演算子++ 及び--は,そのインスタンス型又はそれから派生した型を返さなければならない。
− 二項演算子の少なくとも一つの仮引数は,その二項演算子を定義するインスタンス型でなければなら
ない。
− 変換演算子の仮引数型又は返却値型のいずれかは,その変換演算子を定義するインスタンス型でなけ
ればならない。
例 次に,総称クラスにおける有効な演算子宣言の例を示す。
class X<T>
{
public static X<T> operator ++(X<T> operand) {…}
public static int operator *(X<T> op1, int op2) {…}
public static explicit operator X<T>(T value) {…}
}
型Sから型Tへの変換を行う変換演算子に対して,17.9.3で規定する規則を適用する場合,S又はTに
関連する型仮引数は,他の型と継承関係をもたない一意な型と考え,それら型仮引数に関する制約は無視
する。
注記 最後の文は,規定として冗長で誤解を生じやすいので,次のとおりに考えるのがよい。
型Sから型Tへの変換を行う変換演算子に対して,17.9.3で規定する規則を適用する場合,S
又はTに関連する型仮引数は,一意な型と考える。
例 次を考える。
class C<T> {…}
class D<T>: C<T>
{
public static implicit operator C<int>(D<T> value) {…}
// Ok
public static implicit operator C<string>(D<T> value) {…} // Ok
public static implicit operator C<T>(D<T> value) {…}
//
Error
}
433
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
17.9.3の意図するところによって,T,int及びstringは,それぞれ,関係をもたない一意な型と考え
られるので,最初の二つの演算子宣言は許可される。しかし,三番目の演算子は,C<T>がD<T>の基底ク
ラスなので,誤りとなる。
注記 これらの規則の結果として,次の少し奇妙な記述例も可能になる。
class C<T, U> { }
class D<T, U> : C<T, U>
{
public static implicit operator C<T, U>(D<T, U> value) { ... } //
invalid
public static implicit operator C<U, T>(D<T, U> value) { ... } //
OK
}
最初の場合は,すべてのT及びUに対してC<T,U>がD<T,U>の基底クラスとなるので正し
くない。しかし,二番目の場合にはこれはいえず,記述可能になる。そこで,不正な演算子を
取り除いた記述で,次を考えることができる。
D<int, int> dii = new D<int, int>();
C<int, int> cii = dii;
これは,C<int, int>がD<int, int>の基底クラスであるにもかかわらず,この規格に従
えば正しいように思われる。この問題は,現在,国際規格の段階で検討中である。
型実引数を使って,あらかじめ定義された変換として既に存在している変換を指定する演算子を宣言す
ることができる。
例 次を考える。
struct Convertible<T>
{
public static implicit operator Convertible<T>(T value) {…}
public static explicit operator T(Convertible<T> value) {…}
}
型objectがTの型実引数として指定された場合,二番目の演算子は,既に存在する変換を宣
言することになる。そこで,任意の型から型objectへの暗黙の変換であって,しかも明示的変
換でもある変換が存在することになる。
あらかじめ定義された変換が二つの型の間で存在する場合,それらの型の間の利用者定義の変換は無視
される。特に,次のとおりとする。
− あらかじめ定義された型Sから型Tへの暗黙の変換(13.1参照)が存在する場合,すべての型Sから
型Tへの(暗黙的な又は明示的な)利用者定義の変換は無視される。
− あらかじめ定義された型Sから型Tへの明示的変換(13.2参照)が存在する場合,あらゆる型Sから
型Tへの明示的な利用者定義の変換は無視される。しかし,型Sから型Tへの暗黙的な利用者定義の
変換は,考えられる。
例 object以外のすべての型に対して,一つ前の例のConvertible<T>型は,あらかじめ定義され
た変換と問題を起こさない。例えば,次のとおり。
434
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void F(int i, Convertible<int> n) {
i = n;
// Error
i = (int)n;
// User-defined explicit
conversion
n = i;
// User-defined implicit
conversion
n = (Convertible<int>)i;
// User-defined implicit
conversion
}
しかし,型objectに対しては,あらかじめ定義された変換が,例えば次の例では一つを除い
てすべての場合で,利用者定義の変換を隠ぺいする。
void F(object o, Convertible<object> n) {
o = n;
// Pre-defined boxing
conversion
o = (object)n;
// Pre-defined boxing
conversion
n = o;
// User-defined implicit
conversion
n = (Convertible<object>)o;
// Pre-defined unboxing
conversion
}
25.1.11
総称クラスにおける入れ子型
総称クラス宣言は,入れ子型の宣言を含むことができる。取り囲むクラスの型仮引数は,入れ子型の内
部で使用できる。入れ子型の宣言は,その入れ子型だけに適用される追加的な型仮引数を含むことができ
る。
総称クラス宣言内に含まれるすべての型宣言は,暗黙的に総称型宣言とする。総称型の内部で入れ子に
なっている型への参照を記述する場合には,型仮引数を含めて,その包含する構築型は名前をもっていな
ければならない。しかし,外部クラスの内部からの参照の場合には,入れ子型は,限定なしで使用できる。
すなわち,外部クラスのインスタンス型が,入れ子型を構築する際に,暗黙的に使用される。
例 次の例は,Innerから生成される構築型を参照する三つの異なる方法を示す。このうち,最初の
二つは等価になる。
class Outer<T>
{
class Inner<U>
{
public static void F(T t, U u) {…}
}
static void F(T t) {
Outer<T>.Inner<string>.F(t, "abc"); // These two
statements have
435
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Inner<string>.F(t, "abc"); // the same effect
Outer<int>.Inner<string>.F(3, "abc"); // This type is
different
Outer.Inner<string>.F(t, "abc"); // Error, Outer needs type
arg
}
}
入れ子型の型仮引数は,外部型で宣言されたメンバ又は型仮引数を隠ぺいできる。ただし,このプログ
ラム記述は推奨しない。
例
class Outer<T>
{
class Inner<T>
// Valid, hides Outerʼs T
{
public T t;
// Refers to Innerʼs T
}
}
25.2 総称構造体宣言
注記 構造体宣言(18.1参照)は,型仮引数及びその関連する制約を定義できる。
≪構造体宣言≫:
≪属性群≫opt ≪構造体修飾子群≫opt partialopt struct ≪識別子≫ ≪型
仮引数並び≫opt
≪構造体インタフェース群≫opt ≪型仮引数制約群節群≫opt ≪構造体本体≫ ;opt
総称クラス宣言(25.1及びその下位の細分箇条)のための規則は,18.3で注意した例外も含めて,等し
く総称構造体宣言に適用する。
25.3 総称インタフェース宣言
注記 インタフェース宣言(20.1参照)は,型仮引数及びその関連する制約を定義できる。
≪インタフェース宣言≫:
≪属性群≫opt ≪インタフェース修飾子群≫opt partialopt interface ≪識別
子≫ ≪型仮引数並び≫opt
≪インタフェース基底≫opt ≪型仮引数制約群節群≫opt ≪インタフェース本体
≫ ;opt
総称インタフェース宣言は,25.3で示すことを除いて,総称でないインタフェース宣言と同じ規則に従
う。
インタフェース宣言における各型仮引数は,そのインタフェースの宣言空間(10.3参照)の中での名前
を定義する。インタフェースに関する型仮引数の有効範囲(10.7参照)は,≪インタフェース基底≫,≪
型仮引数制約群節群≫及び≪インタフェース本体≫を含む。その有効範囲内で,型仮引数は型として使用
できる。クラスに関する型仮引数に適用されるのと同じ制約(25.1.1参照)が,インタフェースに関する
型仮引数に適用される。
436
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
25.3.1 実装されたインタフェースの一意性
総称型宣言によって実装されたインタフェースは,すべての構築型に対して一意であり続けなければな
らない。この規則なしでは,構築型によっては呼び出す正しいメソッドを決定できなくなる。
例 総称型宣言を次のように書いてもよいと仮定する。
interface I<T>
{
void F();
}
class X<U, V>: I<U>, I<V>
// Error: I<U> and I<V> conflict
{
void I<U>.F() {…}
void I<V>.F() {…}
}
これが許されていたら,次の場合にいずれのコードを実行するか決定できない。
I<int> x = new X<int,int>();
x.F();
総称型宣言のインタフェースの一覧が有効かどうかを決定するために,次の手順を実行する。
− Lを,総称クラス,総称構造体又は総称インタフェースCで直接に指定されたインタフェースの並び
とする。
− Lの中に既に存在するインタフェースの基底インタフェースをLに追加する。
− Lから重複を除く。
− 型実引数をLに代入した後で,Cから生成された可能な構築型がLの中に二つの同一なインタフェー
スを生じる場合には,Cのその宣言は不当とする。制約宣言は,すべての可能な構築型を決定すると
きには,考慮しない。
注記 先ほどの例のクラス宣言Xでは,インタフェースの並びは,I<U>及びI<V>から構成される。
その宣言は,U及びVが同じ型を用いたあらゆる構築型が,これら二つのインタフェースを同
一の型とするので,不当になる。
例 異なる継承のレベルで指定されたインタフェースを統合することができる。
interface I<T>
{
void F();
}
class Base<U>: I<U>
{
void I<U>.F() {…}
}
class Derived<U, V>: Base<U>, I<V>
// Ok
{
void I<V>.F() {…}
}
437
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
これは,Derived<U, V>がI<U>及びI<V>の両方を実装しているが,正当とする。例えば,
次を考える。
I<int> x = new Derived<int,int>();
x.F();
これは,Derivedのメソッドを呼び出す。これは,Derived<int, int>がI<int>を再実装
することになることによる(20.4.4参照)。
25.3.2 インタフェースメンバの明示的な実装
インタフェースメンバの明示的な実装は,総称でないインタフェース型を用いるのと本質的に同じ方法
で,構築インタフェース型を用いて機能する。通常の場合と同様に,インタフェースメンバの明示的な実
装は,どのインタフェースが実装されるかを指示する≪インタフェース型≫によって限定されなければな
らない。この型は,総称でないインタフェース型又は構築インタフェース型とすることができる。
例
interface IList<ElementType>
{
ElementType[] GetElements();
}
interface IDictionary<KeyType,ElementType>
{
ElementType this[KeyType key] { get; }
void Add(KeyType key, ElementType value);
}
class List<ElementType>: IList<ElementType>,
IDictionary<int,ElementType>
{
ElementType[] IList<ElementType>.GetElements() {…}
// Return the element at index
ElementType IDictionary<int,ElementType>.this[int index] {…}
// Add value at the index
void IDictionary<int,ElementType>.Add(int index, ElementType value)
{…}
}
25.4 総称委譲宣言
注記 委譲宣言(22.1参照)は,型仮引数及びその関連する制約を定義できる。
≪委譲宣言≫:
≪属性群≫opt ≪委譲修飾子群≫opt delegate ≪返却値型≫ ≪識別子≫ ≪型
仮引数並び≫opt
( ≪仮引数並び≫opt ) ≪型仮引数制約群節群≫opt ;
総称委譲宣言は,次で示すことを除いて,総称でない委譲宣言と同じ規則に従う。委譲宣言における各
型仮引数は,その委譲宣言に関連する特殊な宣言空間(10.3参照)の中での名前を定義する。委譲宣言に
おける型仮引数の有効範囲(10.7参照)は,≪返却値型≫,≪仮引数並び≫及び≪型仮引数制約群節群≫
438
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
を含む。
他の総称型宣言と同様に,型実引数が,構築委譲型を生成するために与えられなければならない。構築
委譲型の仮引数型及び返却値型は,委譲宣言における各型仮引数に対して,構築委譲型の対応する型実引
数を代入することによって生成される。結果として生じる返却値型及び仮引数型は,どのメソッドが構築
委譲型と整合するかを決定する際に使用される(22.1参照)。
例
delegate bool Predicate<T>(T value);
class X
{
static void Main() {
Predicate<int> p1 =
delegate(int i) {
//...
};
Predicate<string> p2 =
delegate(string s) {
//...
};
}
}
25.5 構築型
総称型宣言は,それ自体,型実引数を適用する方法によって多くの異なる型を形作るための“青写真”
として使用される非束縛総称型を表している。型実引数は,総称型宣言の名前のすぐ後に続く山括弧(<
及び>)内に記述される。少なくとも一つの型実引数をもつ名前の付いた型を構築型という。構築型は,
型名が出現できるC#プログラムのほとんどの場所で使用できる。非束縛総称型は,≪typeof式≫(14.5.11
参照)内だけで使用できる。
構築型は,単純名(14.5.2参照)として,又はメンバにアクセスする場合(14.5.4参照)に,式の中でも
使用できる。
≪名前空間名又は型名≫を評価する場合,正しい個数の型仮引数をもつ総称型だけが考慮される。そこ
で,その型が異なる個数の型仮引数をもつ限り,同じ識別子を使用でき,異なる型と識別される。これは,
同じプログラムに総称クラス及び総称でないクラスを混在する場合に役に立つ。
例
namespace Widgets
{
class Queue {…}
class Queue<ElementType> {…}
}
namespace MyApplication
{
using Widgets;
439
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class X
{
Queue q1;
//Non-generic Widgets.Queue
Queue<int> q2;
// Generic Widgets.Queue
}
}
≪名前空間名又は型名≫生成規則で名前を検索する詳細な規則は,10.8で示す。この生成規則における
あいまい性の解決は,9.2.3で示す。
≪型名≫は,それが直接には型仮引数を指定していなくとも,構築型と識別されるかもしれない。これ
は,型が総称クラス宣言内に入れ子になっている場合に起こり得る。包含する宣言のインスタンス型が,
暗黙的に名前検索に使用される(25.1.1参照)。
例
class Outer<T>
{
public class Inner {…}
public Inner i;
// Type of i is Outer<T>.Inner
}
注記 安全でないコードでは,構築型は,≪非管理型≫として使用してはならない(25.2参照)。
25.5.1 型実引数
型実引数の並びにおける各実引数は,単に,≪型≫とする。
≪型実引数並び≫:
< ≪型実引数群≫ >
≪型実引数群≫:
≪型実引数≫
≪型実引数群≫ , ≪型実引数≫
≪型実引数≫:
≪型≫
型実引数は,構築型又は型仮引数とする。
注記 安全でないコード(箇条27参照)では,≪型実引数≫は,≪ポインタ型≫であってはならない。
各型実引数は,対応する型仮引数に関する制約を満足しなければならない(25.7.1参照)。
25.5.2 開型及び閉型
すべての型は,開型又は閉型として分類される。開型とは,型仮引数を含む型とする。より明確には,
次による。
− 型仮引数は,開型を定義する。
− 配列型は,その要素の型が開型の場合に限り,開型とする。
− 構築型は,その型実引数の一つ以上が開型の場合に限り,開型とする。構築入れ子型は,その型又は
その包含する型の型実引数の一つ以上が開型の場合に限り,開型とする。
閉型は,開型ではない型とする。
実行時には,総称型宣言内のコードすべては,その総称型宣言に型実引数を適用することによって生成
440
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
される閉構築型の文脈で実行される。総称型内の各型仮引数は,特定の実行時型に束縛される。すべての
文及び式の実行時の処理は,常に閉型を用いて行われ,開型は,コンパイル時の処理の間だけに現れる。
各閉構築型は,それ自体の静的変数集合をもち,それらの変数は,他のいかなる閉構築型とも共有され
ない。開型は実行時には存在しないので,開型に関連する静的変数は存在しない。二つの閉構築型は,変
数が同じ非束縛総称型から構築され,対応する型実引数が同じ型の場合に,同じ型とする。
25.5.3 構築型の基底クラス及びインタフェース
構築クラス型は,通常のクラス型と同じように,直接の基底クラスをもつ。総称クラス宣言が基底クラ
スを指定しない場合,基底クラスはobjectとする。総称クラス宣言が基底クラスを指定している場合,
構築型の基底クラスは,その総称クラス宣言の基底クラスの≪型仮引数≫のそれぞれに,構築型の対応す
る≪型実引数≫を代入して得られる。
例 次の総称クラス宣言を与える。
class B<U,V> {…}
class G<T>: B<string,T[]> {…}
構築型G<int>の基底クラスは,B<string,int[]>になる。
同様に,構築クラス型,構築構造型及び構築インタフェース型は,明示的な基底インタフェースの集合
をもつ。明示的な基底インタフェースは,総称型宣言における明示的な基底インタフェース宣言を取って
きて,その基底インタフェース宣言における≪型仮引数≫のそれぞれに,構築型の対応する≪型実引数≫
を代入することによって得られる。
型のすべての基底クラス及び基底インタフェースの集合は,通常どおりに,直近の基底クラス及び基底
インタフェースに対してその基底クラス及び基底インタフェースを再帰的に得ることによって得られる。
例 次の総称クラス宣言を考える。
class A {…}
class B<T>: A {…}
class C<T>: B<IComparable<T>> {…}
class D<T>: C<T[]> {…}
D<int>の基底クラスはC<int[]>,B<IComparable<int[]>>,A及びobjectである。
25.5.4 構築型のメンバ
構築型の継承されないメンバは,そのメンバ宣言における≪型仮引数≫のそれぞれに,構築型の対応す
る≪型実引数≫を代入することによって得られる。代入の処理は,型宣言の意味に基づいて行われ,単な
る字面上の代入ではない。
例 次の総称クラス宣言を考える。
class Gen<T,U>
{
public T[,] a;
public void G(int i, T t, Gen<U,T> gt) {…}
public U Prop { get {…} set {…} }
public int H(double d) {…}
}
構築型Gen<int[],IComparable<string>>は,次のメンバをもつ。
public int[,][] a;
441
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public void G(int i, int[] t, Gen<IComparable<string>,int[]> gt) {…}
public IComparable<string> Prop { get {…} set {…} }
public int H(double d) {…}
総称クラス宣言Genにおけるメンバaの型は,“Tの二次元配列”なので,この構築型におけ
るメンバaの型は,“intの一次元配列の二次元配列”,すなわちint[,][]になる。
構築型の継承メンバも同様の方法で得られる。まず,直近の基底クラスのすべてのメンバを決定する。
その基底クラスそれ自体が構築型の場合には,ここでの規則を再帰的に適用することになるかもしれない。
次に,継承メンバのそれぞれを,そのメンバ宣言における≪型仮引数≫のそれぞれに,構築型の対応する
≪型実引数≫を代入することによって得る。
例
class B<U>
{
public U F(long index) {…}
}
class D<T>: B<T[]>
{
public T G(string s) {…}
}
この例では,構築型D<int>は,型仮引数Tに型実引数intを代入して得られる継承されない
メンバpublic int G(string s)をもつ。D<int>は,クラス宣言Bからの継承メンバももつ。
これは,まず,UにT[]を代入して構築型B<T[]>のメンバを決定して,public T[] F(long
index)を得る。次に,型実引数intを型仮引数Tに代入して,継承メンバpublic int[] F(long
index)を得る。
25.5.5 構築型のアクセス可能性
構築型C<T1, ...,TN>は,その構成要素C,T1,...,TNがすべてアクセス可能な場合にアクセス可能とす
る。より正確には,構築型のアクセス可能領域は,非束縛総称型のアクセス可能領域及び型実引数群のア
クセス可能領域群の共通領域とする。
25.5.6 変換
構築型は,総称でない型と同じ変換規則(箇条13参照)に従う。これらの規則を適用する場合には,構
築型の基底クラス及び基底インタフェースを25.5.3で示したとおりに決定しなければならない。
構築参照型の間に箇条13で示した以外の特別な変換は存在しない。特に,配列型とは異なり,構築参照
型は共変的な変換を許可しない(19.5参照)。これは,型List<B>は,BがAから派生していたとしても
(暗黙的にも明示的にも)List<A>への変換をもたない。同様に,List<B>からList<object>への変
換も存在しない。
注記 この理由は簡単である。List<B>からList<A>への変換が許されたとしたら,一見,型Aの
値をList<B>に格納できる。しかし,これは,型List<B>のリストの中のすべてのオブジェ
クトが型Bの値であるという不変性を破ることになる。そうしないと,集団クラスに代入した
ときに予期しない失敗が発生する。
例 変換及び実行時型検査の動作を次に示す。
class A {…}
442
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class B: A {…}
class Collection {…}
class List<T>: Collection {…}
class Test
{
void F() {
List<A> listA = new List<A>();
List<B> listB = new List<B>();
Collection c1 = listA;
// Ok, List<A> is a Collection
Collection c2 = listB;
// Ok, List<B> is a Collection
List<A> a1 = listB;
// Error, no implicit
conversion
List<A> a2 = (List<A>)listB; // Error, no explicit conversion
}
}
25.5.7 using別名指令
using別名指令によって,閉構築型に名前を与えることができる。しかし,型実引数を提供することな
しに,総称型宣言に名前を与えてはならない。
例
namespace N1
{
class A<T>
{
class B {}
}
}
namespace N2
{
using W = N1.A;
// Error, cannot name generic type
using X = N1.A.B;
// Error, cannot name generic type
using Y = N1.A<int>;
// Ok, can name closed constructed type
}
25.6 総称メソッド
総称メソッドは,その宣言に≪型仮引数並び≫を含むメソッドとする(17.5参照及び20.2.1参照)。総称
メソッドは,それ自体が総称型でも総称でない型でもよいクラス宣言,構造体宣言又はインタフェース宣
言の内部で宣言できる。総称メソッドが総称型宣言の内部で宣言されている場合,メソッドの本体は,メ
ソッドの型仮引数及び包含する宣言の型仮引数の両方を参照できる。
総称メソッド宣言の≪型仮引数並び≫及び≪型仮引数制約群節群≫は,総称型宣言におけるのと同じ構
文及び目的をもつ。メソッドの≪型仮引数≫は,≪メソッド宣言≫全体を有効範囲とし,その有効範囲を
通じて,≪属性群≫を除く,≪返却値型≫,≪メソッド本体≫及び≪型仮引数制約群節群≫において,型
443
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
を構成するのに使用できる。
メソッドの型仮引数の名前は,同じメソッドの通常の仮引数の名前と同じにはできない。
例 次の例は,存在する場合には,与えられた委譲testを満たす配列の最初の要素を見つける。総
称委譲は,25.4で示す。
public delegate bool Test<T>(T item);
public class Finder
{
public static T Find<T>(T[] items, Test<T> test) {
foreach (T item in items) {
if (test(item))
return item;
}
throw new InvalidOperationException("Item not found");
}
}
25.6.1 総称メソッド呼出し情報
呼出し情報の比較のためには,≪型仮引数制約群節群≫は無視する。メソッドの≪型仮引数≫の名前も
無視する。しかし,総称型の型仮引数の数は意味がある。型仮引数の左から右への順番での位置も意味が
ある(10.6参照)。
例 次の例は,メソッド呼出し情報がこの規則によってどのように影響を受けるかを示す。
class A {}
class B {}
interface IX
{
T F1<T>(T[] a, int i); // Error, both declarations have the same
void F1<U>(U[] a, int i);// signature because return type and type
// parameter names are not significant
void F2<T>(int x); // Ok, the number of type parameters is
void F2(int x); // part of the signature
void F3<T>(T t) where T: A; // Error, constraints are not
void F3<T>(T t) where T: B; // considered in signatures
}
25.6.2 仮想総称メソッド
総称メソッドは,abstract修飾子,virtual修飾子及びoverride修飾子を用いて宣言できる。上
書き又はインタフェース実装のためにメソッドの一致判定を行う場合には,10.6で示された呼出し情報の
一致規則を使用する。総称メソッドが基底クラスで宣言された総称メソッドを上書きする場合,又は総称
メソッドが基底インタフェースのメソッドの明示的なインタフェースメンバ実装になっている場合,その
メソッドは,≪型仮引数制約群節群≫を指定してはならない。これらの場合,メソッドの型仮引数は,上
書きされる又は実装されるメソッドからの制約を継承する。
例
444
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
abstract class Base
{
public abstract T F<T,U>(T t, U u) where U : T;
public abstract T G<T>(T t) where T: IComparable;
}
interface I
{
bool M<T>(T a, T b) where T : class;
}
class Derived: Base, I
{
public override X F<X,Y>(X x, Y y)
// Ok
{
// The implicit constraint Y : X is inherited
// so y is implicitly convertible to type X.
return y;
}
public override T G<T>(T t)
where T: IComparable // Error ‒ constraints not allowed
{…}
bool I.M<U>(U a, U b)
{
// The implicit constraint U : class is inherited
// so a and b can be compared using the reference type
// equality operators.
return a == b;
}
}
Fの上書きは,型仮引数の名前が異なっていることが許されているので妥当である。
Derived.Fの内部では,型仮引数Yは,Base.Fから継承されたとおりの制約Y : Xを暗黙的
にもつ。Gの上書きは,上書きが型仮引数制約の指定を許されないので,正しくない。Derived
における明示的なメソッド実装I.Mは,インタフェースメソッドからの制約U : classを暗黙
的に継承する。
総称メソッドがインタフェースメソッドを暗黙的に実装する場合,各メソッド型仮引数に与えられた制
約は,インタフェース型仮引数を適切な型実引数で置き換えた後で,両方の宣言で等価でなければならな
い。ただし,メソッド型仮引数は,通常の左から右への位置指定によって識別される。
例
interface I<A, B, C>
{
445
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
void F<T>(T t) where T : A;
void G<T>(T t) where T : B;
void H<T>(T t) where T : C;
}
class Cls : I<object,Cls,string>
{
public void F<T>(T t) {…}
// Ok
public void G<T>(T t) where T : Cls {…}
// Ok
public void H<T>(T t) where T : string {…} // Error
}
メソッドCls.F<T>は,暗黙的にI<object,Cls,string>.F<T>を実装する。 この場合,
Cls.F<T>は,objectがすべての型仮引数に関する暗黙的な制約なので,制約T : objectを
指定する必要はないし,指定することは許されない。メソッドCls.G<T>は,暗黙的に
I<object,Cls,string>.G<T>を実装する。これは,その制約が,インタフェース型仮引数が
対応する型実引数で置き換えられた後で,インタフェースにおける制約と一致することによる。
メソッドCls.H<T>の制約は,封印型(この場合にはstring)を制約として使用できないので,
エラーとなる。制約を省略するのも,暗黙的なインタフェースメソッド実装が制約の一致を要求
するので,エラーとなる。このように, I<object,Cls,string>.H<T>を暗黙的に実装するこ
とはできない。このインタフェースメソッドは,次のとおり,明示的なインタフェースメンバ実
装を用いて実装する必要がある。
class Cls : I<object,Cls,string>
{
…
public void H<U>(U u) where U : class {…}
void I<object,Cls,string>.H<T>(T t)
{
string s = t; // Ok
H<T>(t);
}
}
この例では,明示的なインタフェースメンバ実装が,厳密により弱い制約をもつ公開メソッド
を呼び出す。T : stringの制約はソースコードでは表現可能ではないが,意味的には,tから
sへの代入は,TがT : stringの制約を継承しているので妥当である。
25.6.3 総称メソッドの呼出し
総称メソッドの呼出しは,明示的に型実引数並びを指定できる。そうでない場合には,型実引数並びを
省略して,その型実引数並びを決定する型推論に依存することもできる。総称メソッド呼出しを含むメソ
ッド呼出しのコンパイル時の処理は,14.5.5.1で示す。総称メソッドが型実引数並びを付けずに呼び出さ
れる場合,型推論が,25.6.4で示されるとおりに行われる。
例 次は,型推論が行われ,型実引数が型仮引数並びに代入された後で,多重定義解決がどのように
行われるかを示す。
446
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
class Test
{
static void F<T>(int x, T y) {
Console.WriteLine("one");
}
static void F<T>(T x, long y) {
Console.WriteLine("two");
}
static void Main() {
F<int>(5, 324);
// Ok, prints "one"
F<byte>(5, 324);
// Ok, prints "two"
F<double>(5, 324);
// Error, ambiguous
F(5, 324);
// Ok, prints "one"
F(5, 324L);
// Error, ambiguous
}
}
25.6.4 型実引数に関する推論
総称メソッドが型実引数を指定することなく呼び出される場合,その呼出しに対する型実引数を推論す
るために型推論の処理が試される。型推論があるおかげで,総称メソッドの呼出しに簡便な構文を使用で
き,プログラム作成者が冗長な型情報を指定しなくてもよいようになる。
例 次のメソッド宣言を考える。
class Chooser
{
static Random rand = new Random();
public static T Choose<T>(T first, T second) {
return (rand.Next(2) == 0) ? first : second;
}
}
明示的に型実引数を指定することなしに,Chooseメソッドを呼び出すことができる。
int i = Chooser.Choose(5, 213);
// Calls Choose<int>
string s = Chooser.Choose("foo", "bar");// Calls Choose<string>
型推論を通じて,メソッドに渡される実引数から,型実引数int及びstringが決定される。
型推論は,メソッド呼出しのコンパイル時の処理(14.5.5.1参照)の一部として行われ,呼出しの多重定
義解決の段階の前に行われる。特定のメソッドグループがメソッド呼出しにおいて選定されるが,型実引
数がメソッド呼出しとして指定されていない場合,型推論が,そのメソッドグループにおけるそれぞれの
総称メソッドに適用される。型推論が成功した場合に,推論された型実引数が,それに続く多重定義解決
のための実引数の型を決定するために使用される。多重定義解決が呼び出すメソッドとして一つの総称メ
ソッドを選択する場合,推論された型実引数が,呼出しのための実行時型実引数として使用される。特定
のメソッドに対する型推論が失敗した場合,そのメソッドは,多重定義解決に使用されない。型推論の失
敗は,それ自体ではコンパイル時エラーにはならない。しかし,その場合には,多重定義解決が適用可能
447
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
なメソッドを見つけるのに失敗し,コンパイル時エラーになることが多い。
与えられている実引数の個数がメソッドにおける仮引数の個数と異なる場合には,推論は直ちに失敗す
る。そうでない場合には,まず,メソッドに与えられている各々の正規の実引数に対して独立に型推論が
行われる。この実引数が型Aをもち,対応する仮引数が型Pをもつと仮定する。型推論は,次の手順に従
って,型A及び型Pを関係付けることによって進められる。
− 次が正しい場合には,実引数からは何も推論されない。ただし,型推論は成功する。
・ Pは,いかなるメソッド型仮引数にも関係しない。
・ 実引数は,空型(11.2.7参照)をもつ。
・ 実引数は,無名メソッドである。
・ 実引数は,メソッドグループである。
− Pが配列型であって,Aが同じ階数の配列型の場合,A及びPをそれぞれA及びPの要素の型に置き
換えて,この手順を繰り返す。
− Pが配列型であって,AがIList<>,ICollection<>又はIEnumerable<>の具現化の場合,A及
びPをそれぞれA及びPの要素の型に置き換えて,この手順を繰り返す。
− Pが配列型であって,Aが同じ階数の配列型でもIList<>,ICollection<>又はIEnumerable<>
の具現化でもない場合,型推論は,その総称メソッドに対して失敗する。
− Pがメソッドの型仮引数の場合,型推論はこの実引数に対しては成功し,Aはその型仮引数に対して
推論された型になる。
− これらのいずれでもない場合,Pは構築型でなければならない。Pに出現するメソッド型仮引数MXそ
れぞれに対して,ただ一つの型TXが決定でき,各MXを各TXに置き換えて標準の暗黙的な変換によっ
てAに変換可能な型を生成できる場合,型推論はこの実引数に対して成功し,各TXは各MXに対して
推論された型となる。メソッド型仮引数制約は,存在する場合には,型推論の目的には無視される。
与えられたMXに対してTXが存在しない又は複数存在する場合には,型推論は,その総称メソッドに
対して失敗する。ただし,複数のTXが存在する状況は,Pが総称インタフェース型であって,Aがそ
のインタフェースの多重に構築されたものを実装している場合だけに起こり得る。
メソッドの実引数のすべてがこのアルゴリズムによって成功するように処理される場合,それら実引数
から生成されたすべての推論が保持される。型推論は,次の両方が成立する場合に,与えられた総称メソ
ッド及び実引数並びに対して成功した,という。
− メソッドの型仮引数のそれぞれが,それに対して推論された型実引数をもっている。これを,推論の
集合が完全である,という。
− 型仮引数のそれぞれに対して,その型仮引数に対する推論のすべてが同じ型実引数を推論する。これ
を,推論の集合が無矛盾である,という。
総称メソッドが仮引数配列(17.5.1.4参照)で宣言されている場合,型推論は,最初に,その通常形式で
のメソッドに対して実行される。型推論が成功し,その結果としてメソッドを適用可能になった場合には,
そのメソッドが,その標準形式での多重定義解決の対象になる。型推論が成功しなかった場合には,型推
論がその展開形式(14.4.2.1参照)でのメソッドに対して実行される。
25.6.5 委譲を伴った総称メソッドの使用
総称メソッド宣言を参照するような委譲のインスタンスを生成できる。総称メソッドを参照する委譲生
成式を含む,委譲生成式のコンパイル時の処理の詳細は,14.5.10.3で示す。
委譲を通じて総称メソッドを呼び出すときに使用する型実引数は,その委譲を具現化するときに決定さ
448
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
れる。型実引数は,≪型実引数並び≫によって明示的に与えるか,型推論によって決定できる(25.6.4参
照)。型推論が使用される場合,委譲の仮引数の型が推論処理における実引数の型として使用される。委譲
の返却値型は,推論には使用されない。
例 次は,委譲を具現化する式に型実引数を与える二つの方法を示す。
delegate int D(string s, int i);
delegate int E();
class X
{
public static T F<T>(string s, T t) {…}
public static T G<T>() {…}
static void Main() {
D d1 = new D(F<int>);// Ok, type argument given explicitly
D d2 = new D(F);
// Ok, int inferred as type argument
E e1 = new E(G<int>);// Ok, type argument given explicitly
E e2 = new E(G);
// Error, cannot infer from return type
}
}
総称メソッドが委譲インスタンスを生成するために使用される場合はいつでも,委譲インスタンスが生
成されるときに,型実引数が与えられるか,又は推論される。委譲が呼び出されるときには,≪型実引数
並び≫を与えてはならない(22.3参照)。
25.6.6 総称特性,総称イベント,総称添字子,総称演算子,総称構築子及び総称終了化子の禁止
特性,イベント,添字子,演算子,構築子及び終了化子は,それ自体では,型仮引数をもってはならな
い。ただし,それらは総称型の中に出現することは可能であって,包含する型からの型仮引数を使用する
ことはできる。
25.7 制約
総称型宣言及び総称メソッド宣言は,宣言に≪型仮引数制約群節群≫を含めることによって,型仮引数
制約を指定できる。
≪型仮引数制約群節群≫:
≪型仮引数制約群節≫
≪型仮引数制約群節群≫ ≪型仮引数制約群節≫
≪型仮引数制約群節≫:
where ≪型仮引数≫ : ≪型仮引数制約群≫
≪型仮引数制約群≫:
≪主制約≫
≪副制約群≫
≪構築子制約≫
≪主制約≫ , ≪副制約群≫
≪主制約≫ , ≪構築子制約≫
≪副制約群≫ , ≪構築子制約≫
≪主制約≫ , ≪副制約群≫ , ≪構築子制約≫
449
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪主制約≫:
≪クラス型≫
class
struct
≪副制約群≫:
≪インタフェース型≫
≪型仮引数≫
≪副制約群≫ , ≪インタフェース型≫
≪副制約群≫ , ≪型仮引数≫
≪構築子制約≫:
new ( )
各≪型仮引数制約群節群≫は,トークンwhere,型引数の名前,コロン及びその型仮引数についての制
約の並び,の順番での並びとする。where節の並びの順番は任意だが,各型仮引数については,高々一つ
とする。トークンwhereは,キーワードではない。
注記 whereをキーワードにしないのは,互換性のためである。
where節で与えられる制約の並びには,一つの主制約,一つ以上の副制約,及び構築子制約new()のう
ちの必要なものを,この順番で含むことができる。
主制約は,クラス型,参照型制約のclass又は値型制約のstructのいずれかとする。副制約は,≪
型仮引数≫又は≪インタフェース型≫のいずれかとする。
参照型制約は,その型仮引数のために使用される型実引数が参照型でなければならないことを指定する
(25.7.1参照)。クラス型,インタフェース型,委譲型,配列型又は(この箇条の後の方で定義する)参照
型であると知られる型仮引数は,この制約を満足する。
値型制約は,その型仮引数のために使用される型実引数が値型でなければならないことを指定する
(25.7.1参照)。null許容でない構造体型,enum型又は値型制約をもつ型仮引数は,この制約を満足す
る。値型制約をもつ型仮引数は,≪構築子制約≫をもってはならない。System.Nullable<T>型は,T
のためのnull許容でない値型制約を指定する。このようにして,T??及びNullable<Nullable<T>>
といった再帰的な構築型は禁止される。
ポインタ型は,型実引数になることは許されないので,参照型制約又は値型制約を満足しない。
制約がクラス型,インタフェース型又は型仮引数の場合,その型は,制約のかかる型仮引数のために使
用されるすべての型実引数が提供しなければならない最小の“基底型”を指定する。構築型又は総称メソ
ッドが使用される場合にはいつでも,コンパイル時に,型実引数が,型仮引数についての制約に対して検
査される。与えられる型実引数は,型仮引数に対して与えられた制約のすべてから派生しているか,又は
それら制約のすべてを実装しているかでなければならない。
≪クラス型≫制約は,次の規則を満足しなければならない。
− クラス型でなければならない。
− 封印されていてはならない。
− System.Array,System.Delegate,System.Enum又はSystem.ValueTypeのどれかであって
はならない。
− objectであってはならない。
注記 すべての型はobjectから派生するので,そのような制約は,許したとしても意味がない。
450
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− 与えられた型仮引数についてクラス型制約は一つしか許されない。
≪インタフェース型≫制約として指定される型は,次の規則を満足しなければならない。
− インタフェース型でなければならない。
− 与えられたwhere節の中で二回以上指定されてはならない
いずれの場合も,制約は,構築型の一部としての関連する型又はメソッドの宣言のいかなる型仮引数も
含むことができ,宣言されている型自体をも含むことができる。
型仮引数制約として指定されたいかなるクラス型又はインタフェース型も,少なくとも,宣言されてい
る総称型又は総称メソッドと同じようにアクセス可能(10.5.4参照)でなければならない。
≪型仮引数≫制約として指定される型は,次の規則を満足しなければならない。
− 型仮引数でなければならない。
− 与えられたwhere節の中で二回以上指定されてはならない
さらに,依存性が次によって定義される推移関係の場合,型仮引数の依存性グラフに循環があってはな
らない。
− 型仮引数Tが型仮引数Sに対する制約として使用されている場合, SはTに依存するという。
− 型仮引数Sが型仮引数Tに依存し,Tが型仮引数Uに依存する場合,SはUに依存するという。
この関係が与えられている場合,型仮引数が直接的にも間接的にもそれ自体に依存するのは,コンパイ
ル時エラーとする。
いかなる制約も依存する型仮引数の間で一貫性を保っていなければならない。すなわち,型仮引数Sが
型仮引数Tに依存している場合,次が成立しなければならない。
− Tは,値型制約をもってはならない。もっている場合,Tは実効的には封印されていることになり,
強制的にSはTと同じ型になり,二つの型仮引数は必要でなくなってしまう。
− Sが値型制約をもっている場合,Tは≪クラス型≫制約をもってはならない。
− Sが≪クラス型≫制約Aをもち,Tが≪クラス型≫制約Bをもつ場合,AとBとの間に恒等変換が存
在するか,AからBへの暗黙的な参照変換が存在するか,又はBからAへの暗黙的な参照変換が存在
するか,でなければならない。
− Sが型仮引数Uに依存し,Uが≪クラス型≫制約Aをもち,Tが≪クラス型≫制約Bをもつ場合も,A
とBとの間に恒等変換が存在するか,AからBへの暗黙的な参照変換が存在するか,又はBからAへ
の暗黙的な参照変換が存在するか,でなければならない。
Sが値型制約をもちTが参照型制約をもつのは,正当とする。実際には,これは,TをSystem.Object,
System.ValueType,System.Enum及びインタフェース型に限ることになる。
型仮引数のためのwhere節がnew()という形式の構築子制約を含む場合,型のインスタンスを生成す
るためにnew演算子を使うことができる(14.5.10.1参照)。構築子制約とともに型仮引数に対して使用さ
れるあらゆる型実引数は,(すべての値型からの生成を含む)公開の仮引数なしの構築子をもたなければな
らないか,値型制約又は構築子制約をもつ型仮引数でなければならない(詳細は25.7.1参照。)。
例 次に制約の例を示す。
interface IPrintable
{
void Print();
}
interface IComparable<T>
451
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
int CompareTo(T value);
}
interface IKeyProvider<T>
{
T GetKey();
}
class Printer<T> where T: IPrintable {…}
class SortedList<T> where T: IComparable<T> {…}
class Dictionary<K, V>
where K: IComparable<K>
where V: IPrintable, IKeyProvider<K>, new()
{
…
}
次の例は,型仮引数の依存性グラフに循環を生じるので,エラーになる。
class Circular<S, T>
where S: T
where T: S
// Error ‒ circularity in dependency graph
{...}
さらに次の例は,様々な正しくない状況を示している。
class Sealed<S, T>
where S: T
where T: struct
// Error ‒ T is sealed
{...}
class A {...}
class B {...}
class Incompat<S, T>
where S: A, T
where T: B
// Error ‒ incompatible class-type constraints
{...}
class StructWithClass<S, T, U>
where S: struct, T
where T: U
where U: A
// Error ‒ A incompatible with struct
{...}
型仮引数Tの実効基底クラスを次のとおりに定義する。
− Tが主制約も型仮引数制約ももたない場合,その実効基底クラスはobjectとする。
− Tが値型制約をもつ場合,その実効基底クラスはSystem.ValueTypeとする。
− Tが参照型制約をもつが≪型仮引数≫制約をもたない場合,その実効基底クラスはobjectとする。
452
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− Tが≪クラス型≫制約Cをもつが≪型仮引数≫制約をもたない場合,その実効基底クラスはCとする。
− Tは主制約をもたないが複数の≪型仮引数≫制約をもつ場合,その実効基底クラスは,その≪型仮引
数≫制約の実効基底クラスの集合の中で最も内側の型(13.4.2参照)とする。一貫性規則は,そのよ
うな最も内側の型が存在することを保証する。
− Tが≪クラス型≫制約及び複数の≪型仮引数≫制約の両方をもつ場合,その実効基底クラスは,Tの
≪クラス型≫制約とその≪型仮引数≫制約の実効基底クラスとから成る集合の中で最も内側の型
(13.4.2参照)とする。一貫性規則は,そのような最も内側の型が存在することを保証する。
− Tが参照型制約及び複数の≪型仮引数≫制約の両方をもつ場合,その実効基底クラスは,その≪型仮
引数≫制約の実効基底クラスから成る集合の中で最も内側の型(13.4.2参照)とする。一貫性規則は,
そのような最も内側の型が存在することを保証する。
型仮引数Tの実効インタフェース集合を次のとおりに定義する。
− Tが≪副制約群≫制約をもつ場合,その実効インタフェース集合は,空とする。
− Tが≪インタフェース型≫制約をもつが≪型仮引数≫制約をもたない場合,その実効インタフェース
集合は,≪インタフェース型≫制約の集合とする。
− Tが≪インタフェース型≫制約をもたないが≪型仮引数≫制約をもつ場合,その実効インタフェース
集合は,≪型仮引数≫制約の実効インタフェース集合の合併集合とする。
− Tが≪インタフェース型≫制約及び≪型仮引数≫制約の両方をもつ場合,その実効インタフェース集
合は,≪インタフェース型≫制約の集合及び≪型仮引数≫制約の実効インタフェース集合の合併集合
とする。
型仮引数は,次のいずれかの場合に,参照型であると知られている,という。
− 参照型制約をもっている。
− その実効基底クラスが,objectでも System.ValueTypeでもない。
制約された型仮引数の型の値は,制約によって暗示されるインスタンスメンバにアクセスするために使
用できる。
例 次を考える。
interface IPrintable
{
void Print();
}
class Printer<T> where T: IPrintable
{
void PrintOne(T x) {
x.Print();
}
}
IPrintableのメソッドは,Tが常にIPrintableを実装するように制約されているので,
直接的にx上で呼び出される。
同じプログラムにおける二つの部分総称型宣言は,それらが(型仮引数の個数を含む)同じ完全限定名
をもつ場合に,同じ非束縛総称型に寄与する(10.3参照)。そうした二つの部分型宣言は,型仮引数のそれ
ぞれに対して,順番に同じ名前を指定しなければならない。
453
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
部分総称型宣言が制約を含む場合,その制約は,制約を含むすべての他の部分と一致しなければならな
い。特に,制約を含む各部分は,型仮引数の同じ集合に対して制約をもたなければならないし,各型仮引
数に対して,主制約,副制約及び構築子制約の集合は等価でなければならない。ただし,制約の二つの集
合は,それらが同じメンバを含む場合に,等価とする。部分総称型のいずれの部分も型仮引数に制約を指
定していない場合,その型仮引数は,制約なし,とする。
例
partial class Map<K,V>
where K: IComparable<K>
where V: IKeyProvider<K>, new()
{
...
}
partial class Map<K,V>
where V: IKeyProvider<K>, new()
where K: IComparable<K>
{
...
}
partial class Map<K,V>
{
...
}
制約を含む(最初の二つの)部分が,実効的に,主制約,副制約及び構築子制約の同じ集合を
型仮引数の同じ集合に対して,それぞれ指定しているので,これは正しい。
25.7.1 制約の充足
構築型又は総称メソッドを参照する場合,与えられる型実引数は,その総称型又は総称メソッドで宣言
されている型仮引数制約に関して検査される。各where節に対して,名前付きの型仮引数に対応する型実
引数Aが,次のとおりに各制約に対して検査される。
注記 ここではAは型実引数だが,次では,Aが型仮引数になっている箇所がある。これは一見矛盾
しているが,型実引数Aは元になる型仮引数Tに与えられる(制約を満たした)何らかの型で
しかない(25.5.1参照)ので,A自体が型仮引数になることもある。例えば,
class Foo<FTypeParam> { ... } where FTypeParam ...
class Woo<WTypeParam>
{
Foo<int> // (a)
Foo<WTypeParam> // (b)
}
ここで,(a)の場合はFoo<T>の型仮引数TがFtypeParamで型実引数Aがintで定まっ
た実際の型が渡されているが,(b)の場合はFoo<T>の型仮引数TがFtypeParamで型実引数
454
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
AがWTypeParamで型仮引数が渡されている。つまり,型実引数Aは,渡される型によっては,
それ自体が型仮引数になることもある。そのような場合には,次では,渡される型を区別する
意味で“型仮引数A”などといっている。
− 制約がクラス型,インタフェース型又は型仮引数の場合,制約に現れるあらゆる型仮引数に,与えら
れた型実引数を代入した後の制約をCで表すとする。この制約を満足するためには,型Aは,次の一
つによって,型Cに変換可能な場合でなければならない。
・ 恒等変換(13.1.1参照)。
・ 暗黙の参照変換(13.1.4参照)。
・ 型Aがnull許容でない値型の場合のボックス化変換(13.1.5参照)。
・ 型仮引数Aから Cへの,暗黙の参照変換,ボックス化変換又は型仮引数変換(25.7.4参照)。
− 制約が参照型制約の場合,型Aは,次の一つを満足しなければならない。
・ Aが,インタフェース型,クラス型,委譲型又は配列型になっている。
注記 System.ValueType及びSystem.Enumは,この制約を満たす参照型である。
・ Aが,参照型であると知られている型仮引数になっている(25.7参照)。
− 制約が値型制約の場合,型Aは,次の一つを満足しなければならない。
・ Aが,null許容でない構造型又はenum型になっている。
注記 System.ValueType及びSystem.Enumは,参照型であって,この制約を満たさない。
・ Aが,値型制約をもつ型仮引数になっている(25.7参照)。
・ Aが,構築子制約new()をもつ型仮引数になっている。
注記 最後の項目は,25.7.1の冒頭で追加された注記に関する国際のC#グループとの議論からの
派生として抜けがあることが分かり,追加された。
− 制約が構築子制約new()の場合,型実引数Aはabstractであってはならず,公開の仮引数なしの
構築子をもっていなければならない。これは,次の一つが成立する場合に満足される。
・ Aが値型になっている。これは,すべての値型が公開の省略時構築子をもつことによる(11.1.2参
照)。
・ Aは,構築子制約をもつ型仮引数になる(25.7参照)。
・ Aが,値型構築子をもつ型仮引数になっている(25.7参照)。
・ Aが,abstractではないクラスであって,明示的に宣言された仮引数なし公開構築子を含んでい
る。
・ Aはabstractでなく,省略時構築子をもつ(17.10.4参照)。
型仮引数の制約の一つ以上が与えられた型実引数によって満足されない場合,コンパイル時エラーが発
生する。
型仮引数は継承されないので,制約は,決して継承されない。
例 次のコードで,Dは,基底クラスB<T>によって課せられた制約を型仮引数Tが満足する結果と
して,型仮引数Tに関する制約を指定する必要がある。反対に,クラスEは,List<T>があらゆ
るTに対してIEnumerableを実装するので,制約を指定しなくてよい。
class B<T> where T: IEnumerable {…}
class D<T>: B<T> where T: IEnumerable {…}
class E<T>: B<List<T>> {…}
455
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
25.7.2 型仮引数が関係するメンバ検索
型仮引数Tによって与えられる型におけるメンバ検索の結果は,Tに対して指定された制約が存在する
場合には,その制約に依存する。Tが≪クラス型≫,≪インタフェース型≫又は≪型仮引数≫の制約をも
たない場合,Tに関するメンバ検索は,objectに関するメンバ検索と同じメンバの集合を返す。そうで
ない場合には,メンバ検索の第1段階(14.3参照)では,Tの実効基底クラスにおけるすべてのメンバ,
及びTの実効インタフェース集合の各インタフェースにおけるすべてのメンバを考慮する。これらの型の
それぞれに対してメンバ検索の第1段階を実行した後で,sの結果を結合し,隠ぺいされるメンバを結合
した結果から取り除く。
総称性を導入する前には,メンバ検索は,常に,クラスで独立に宣言されたメンバの集合か,又はイン
タフェース及び可能な場合は型objectで独立に宣言されたメンバの集合のいずれかを返した。型仮引数
が関係するメンバ検索は,これを幾分か変更する。型仮引数がobject以外の実効基底クラス及び空でな
い実効インタフェース集合の両方をもつ場合,メンバ検索は,クラスで宣言されたメンバ及びインタフェ
ースで宣言されたメンバ,それらの集合を返すことができる。次の追加規則が,この場合を取り扱う。
− 14.3で規定したとおり,メンバ検索中は,object以外のクラスで宣言されたメンバがインタフェー
スで宣言されたメンバを隠ぺいする。
− メソッド(14.5.5.1参照)及び添字子(14.5.6.2参照)の多重定義解決中は,適用可能なメンバがobject,
以外のクラスで宣言されている場合,インタフェースで宣言されたすべてのメンバは,考慮するメン
バの集合から取り除く。
これらの規則は,型仮引数に関する束縛がobject以外の実効基底クラス及び空でない実効インタフェ
ース集合の両方を伴って行われる場合だけに,適用される。
注記 非形式的に簡単にいうと,クラス型制約の中で定義されたメンバは,インタフェース制約の中
のメンバよりも優先される。
25.7.3 型仮引数及びボックス化
構造型が(Equals,GetHashCode又は ToStringといった)System.Objectから継承された仮想
メソッドを上書きする場合,構造型のインスタンスを通じての仮想メソッドの呼出しは,ボックス化の発
生を引き起こさない。これは,構造型が型仮引数として使用され,呼出しがその型仮引数型のインスタン
スを通じて行われる場合であっても,正しいとする。
例
using System;
struct Counter
{
int value;
public override string ToString() {
value++;
return value.ToString();
}
}
class Program
{
static void Test<T>() where T: new() {
456
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
T x = new T();
Console.WriteLine(x.ToString());
Console.WriteLine(x.ToString());
Console.WriteLine(x.ToString());
}
static void Main() {
Test<Counter>();
}
}
このプログラムは,次を出力する。
1
2
3
ToStringが副作用をもつのは推奨しない記述だが,この例は,x.ToString()の三つの呼出
しに対してボックス化が発生しなかったことを示している。
同様に,制約された型仮引数におけるメンバにアクセスする場合,ボックス化は暗黙的に発生しない。
例 インタフェースICounterが,値を修正するために使用できるメソッドIncrementを含むとす
る。ICounterが制約として使用される場合,Incrementメソッドの実装は,Incrementがそ
れに基づいて呼び出された変数への参照を伴って呼び出される。決してボックス化されたコピー
を伴って呼び出されるのではない。この動作は,総称でない場合とは異なっている。構造型にお
いてインタフェース実装を呼び出す場合には,実引数は常にボックス化される。
Using System;
Interface ICounter
{
void Increment();
}
Struct Counter: ICounter
{
int value;
Public override string ToString() {
return value.ToString();
}
Void ICounter.Increment() {
value++;
}
}
Class Program
{
static void Test<T>() where T: ICounter, new() {
T x = new T();
457
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
Console.WriteLine(x);
x.Increment();
// Modify x
Console.WriteLine(x);
((ICounter)x).Increment(); // Modify boxed copy of x
Console.WriteLine(x);
}
Static void Main() {
Test<Counter>();
}
}
最初のIncrementメソッドの呼出しは,変数xにおける値を修正する。これは,Increment
への二番目の呼出しとは等価ではない。二番目の場合は,xのボックス化されたコピーにおける
値を修正する。このようにして,プログラムは,次を出力する。
0
1
1
25.7.4 型仮引数を含んだ変換
型仮引数Tについて許される変換は,Tに対して指定された制約に依存し,箇条13で詳細に示す。
変換規則は,制約のない型仮引数から任意のインタフェースでない型への直接的な明示的変換を許さな
い。このことは,意外かもしれない。この規則の理由は,混乱を生じないようにし,それら変換の意味を
明確にすることによる。
例 次の宣言を考える。
class X<T>
{
public static long F(T t) {
return (long)t;
// Error, explicit conversion not
permitted
}
}
tのlongへの直接的な明示的変換が許されていれば,容易に,X<int>.F(7)は7Lを返すと
期待するだろう。しかし,型がコンパイル時に数値型と知られている場合にだけ,標準的な数値
変換が考慮されるので,これは起こらない。意味を明確にすると,この例は,次のとおりになる。
class X<T>
{
public static long F(T t) {
return (long)(object)t;
// OK, conversions permitted
}
}
このコードはコンパイルできるが,X<int>.F(7)を実行すると,実行時に例外を送出する。
これは,ボックス化されたintは直接にlongに変換できないことによる。
458
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
26 反復子
反復子は,返却値の型が列挙子インタフェース又は列挙可能インタフェースである関数メンバを実装す
る手段である。この関数メンバは,反復子によって生成される順番どおりの一連の値を返す。
例 次に示すStack<T>クラスは,反復子を使用してGetEnumeratorメソッドを実装している。こ
の反復子は,スタックの要素を上から下に順番に列挙する。
using System.Collections.Generic;
public class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
public void Push(T data) {…}
public T Pop() {…}
public IEnumerator<T> GetEnumerator() {
for (int i = count ‒ 1; i >= 0; --i) {
yield return items[i];
}
}
}
反復子はyield文を使用して実装されている。yield文は返却値の型が列挙子インタフェー
スのメソッドでだけ使用できる。GetEnumeratorメソッドが存在するのでStack<T>は列挙可
能な型であり,Stack<T>のインスタンスはforeach文で使用できる。次の例では整数のスタッ
クに0から9までの値を入れ,foreachループを使ってスタックの値を上から下に順番に表示し
ている。
using System;
class Test
{
static void Main() {
Stack<int> s = new Stack<int>();
for (int i = 0; i < 10; i++) s.Push(i);
foreach (int i in s) Console.Write("{0} ", i);
Console.WriteLine();
}
}
この例の出力は次のようになる。
9 8 7 6 5 4 3 2 1 0
26.1 反復子ブロック
反復子ブロックは,一連の値を順番に生成するブロック(15.2参照)とする。反復子ブロックは,yield
文(15.14参照)が一つ以上含まれていることが通常の文と異なる。
− yield return文は,繰返しの次の値を生成する。
− yield break文は,繰返しが完了したことを示す。
459
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
反復子ブロックは,該当する関数メンバの返却値の型が,列挙子インタフェース(26.1.1参照)又は列
挙可能インタフェース(26.1.2参照)のうちの一つである場合,メソッド本体(17.5参照),演算子本体(17.9
参照)又はアクセス子本体(17.6.2参照)として使用できる。
反復子ブロックは,C#の文法において特別な要素ではない。反復子ブロックには幾つかの制限事項があ
り,関数メンバの宣言の意味論に大きな影響を与える。しかし,文法的にはただのブロックに過ぎない。
関数メンバが反復子ブロックを使用して実装された場合,関数メンバの仮引数並びに対してref仮引数
又はout仮引数を指定するとコンパイル時エラーになる。
反復子ブロックにreturn文を使用するとコンパイル時エラーになる。ただし,yield return文は使
用できる。
反復子ブロックに安全でない文脈(27.1参照)が含まれる場合,コンパイル時エラーになる。反復子ブ
ロックは,自身の宣言が安全でない文脈内に入れ子になっている場合でも,常に安全な文脈を定義する。
26.1.1 列挙子インタフェース
列挙子インタフェースは,総称でないインタフェースSystem.Collections.IEnumerator及び,総
称インタフェースSystem.Collections.Generic.IEnumerator<T>を具現化したインタフェースす
べてを指す。簡単にするため,この規格中ではこれらのインタフェースをそれぞれIEnumerator及び
IEnumerator<T>と呼ぶ。
26.1.2 列挙可能インタフェース
列挙可能インタフェースは,総称でないインタフェースSystem.Collections.IEnumerable及び,
総称インタフェースSystem.Collections.Generic.IEnumerable<T>を具現化したインタフェース
すべてを指す。簡単にするため,この規格中ではこれらのインタフェースをそれぞれIEnumerable及び
IEnumerable<T>と呼ぶ。
26.1.3 産出型
反復子ブロックは,すべて同じ型の一連の値を生成する。この型を,反復子ブロックの産出型と呼ぶ。
− IEnumerator又はIEnumerableを返す関数メンバの実装に使用される反復子ブロックの産出型
は,objectである。
− IEnumerator<T>又はIEnumerable<T>を返す関数メンバの実装に使用される反復子ブロックの産
出型は,Tである。
26.1.4 thisによるアクセス
クラスのインスタンスメンバの反復子ブロック中では,this式は値に分類される。値の型はthis式
が使用されているクラスであり,値はメンバが呼び出されたオブジェクトへの参照とする。
構造体のインスタンスメンバの反復子ブロック中では,this式は変数に分類される。変数の型は,this
式が使用されている構造体とする。この変数は,メンバが呼び出された構造体のコピーを表す。構造体の
インスタンスメンバの反復子ブロック中のthis変数は,その構造体型の値仮引数と全く同様の動作をす
る。
注記 これは,反復子でない関数メンバ本体における扱いと異なる。
26.2 列挙子オブジェクト
列挙子インタフェース型を返す関数メンバが,反復子ブロックを使用して実装されている場合,その関
数メンバを呼び出すとすぐに反復子ブロックのコードが実行されるわけではない。実際には,列挙子オブ
ジェクトが作成されて返される。このオブジェクトは反復子ブロックに記述されたコードをカプセル化し,
列挙子オブジェクトのMoveNextメソッドが呼び出されたときに反復子ブロック内のコードが実行され
460
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る。列挙子オブジェクトは次のような特徴をもつ。
− IEnumerator及びIEnumerator<T>を実装する。Tは反復子ブロックの産出型である。
− System.IDisposableを実装する。
− 関数メンバに渡される実引数の値のコピー(もしあれば)及びインスタンス値で初期化される。
− 四つの状態をとる。列挙前,実行中,停止中及び列挙後である。最初は列挙前状態である。
列挙子オブジェクトは通常,反復子ブロックのコードをカプセル化して列挙子インタフェースを実装し
た,コンパイラによって生成される列挙子クラスのインスタンスであるが,他の実装方法も可能である。
列挙子型がコンパイラによって生成される場合,そのクラスは直接的又は間接的に関数メンバを含む型に
入れ子になり,privateのアクセス可能性をもち,コンパイラが使用するために予約された名前(9.4.2
参照)をもつ。
列挙子オブジェクトは,上記以外のインタフェースも実装可能である。
次節以降では,列挙子オブジェクトのIEnumerableインタフェース及びIEnumerable<T>インタフ
ェースの実装によるMoveNextメンバ,Currentメンバ及びDisposeメンバの正確な動作について述べ
る。
列挙子オブジェクトは,IEnumerator.Resetメソッドを実装しない。このメソッドを呼び出すと
System.NotSupportedExceptionが送出される。
26.2.1 MoveNextメソッド
列挙子オブジェクトのMoveNextメソッドは,反復子ブロックのコードをカプセル化している。
MoveNextメソッドを呼び出すと反復子ブロックのコードが実行され,列挙子オブジェクトのCurrent
特性が適切な値に設定される。MoveNextの正確な動作は,MoveNextが呼び出されたときの列挙子オブ
ジェクトの状態に依存する。
− 列挙子オブジェクトの状態が列挙前の場合,MoveNextを呼び出すと次の動作を行う。
・ 状態を実行中に変更する。
・ 反復子ブロックの仮引数(thisを含む)を,列挙子オブジェクトが初期化されたときに保存され
た実引数の値及びインスタンス値に初期化する。反復子ブロックを先頭から実行し,中断されるま
で実行する(中断については後述)。
− 列挙子オブジェクトの状態が実行中の場合,MoveNextの呼出しの結果は未規定である。
− 列挙子オブジェクトの状態が停止中の場合,MoveNextを呼び出すと次の動作を行う。
・ 状態を実行中に変更する。
・ すべての局所変数及び仮引数(thisを含む)の値を,反復子ブロックの実行が前回停止されたと
きに保存された値に復元する(これらの変数が参照するオブジェクトの内容が,前回MoveNextを
呼び出したときから変わっている可能性があることに注意)。
・ 前回実行が停止されたyield return文の直後から反復子ブロックの実行を再開し,中断されるま
で実行を続ける(中断についてはこの箇条書きの後に示す。)。
− 列挙子オブジェクトの状態が列挙後の場合,MoveNextを呼び出すとfalseを返す。
MoveNextの呼出しで反復子ブロックが実行される場合,次に述べる四つの場合に実行が中断される。
四つの場合とは,yield return文が実行された場合,yield break文が実行された場合,反復子ブロ
ックが終了した場合,例外が送出されて反復子ブロックの外に伝ぱ(播)された場合である。
− yield return文が実行された場合(15.14参照):
・ 文の式が評価され,暗黙的に産出型に変換されて,列挙子オブジェクトのCurrent特性に設定さ
461
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
れる。
・ 反復子本体の実行が停止される。すべての局所変数及び仮引数(thisを含む)の値が,yield
return文の位置のまま保存される。yield return文が一つ以上のtryブロックに囲まれている
場合でも,関連するfinallyブロックはこの時点では実行されない。
・ 列挙子オブジェクトの状態が停止中に変更される。
・ MoveNextメソッドはtrueを呼出し元に返し,繰返しが次の値に正常に進んだことを示す。
− yield break文が実行された場合(15.14参照):
・ yield break文が一つ以上のtryブロックに囲まれている場合,関連するfinallyブロックが実
行される。
・ 列挙子オブジェクトの状態が列挙後に変更される。
・ MoveNextメソッドはfalseを呼出し元に返し,繰返しが完了したことを示す。
− 反復子ブロックが終了した場合:
・ 列挙子オブジェクトの状態が列挙後に変更される。
・ MoveNextメソッドはfalseを呼出し元に返し,繰返しが完了したことを示す。
− 例外が送出されて反復子ブロックの外に伝ぱ(播)された場合:
・ 例外の伝ぱ(播)によって,反復子本体内の適切なfinallyブロックが実行される。
・ 列挙子オブジェクトの状態が列挙後に変更される。
・ MoveNextメソッドの呼出し元まで例外が伝ぱ(播)する。
26.2.2 Current特性
列挙子オブジェクトのCurrent特性は,反復子ブロックのyield return文によって影響を受ける。
列挙子オブジェクトが停止中状態のとき,Currentの値は前回のMoveNextの呼出しで設定された値
とする。列挙子オブジェクトが列挙前,実行中又は列挙後状態のとき,Currentにアクセスした結果は未
規定である。
産出型がobjectではない反復子ブロックにおいて,Currentに列挙子オブジェクトのIEnumerable
の実装を通してアクセスした結果は,Currentに列挙子オブジェクトのIEnumerator<T>の実装を通し
てアクセスして,その値をobjectにキャストした結果と同じである。
26.2.3 Disposeメソッド
Disposeメソッドは,列挙子オブジェクトを列挙後状態にすることで繰返しを完了させるために使用す
る。
− 列挙子オブジェクトの状態が列挙前の場合,Disposeを呼び出すと状態が列挙後に変更される。
− 列挙子オブジェクトの状態が実行中の場合,Disposeの呼出しの結果は未規定である。
− 列挙子オブジェクトの状態が停止中の場合,Disposeを呼び出すと次の動作を行う。
・ 状態を実行中に変更する。
・ 最後に実行されたyield return文がyield break文であったかのようにfinallyブロックを
すべて実行する。これによって例外が送出されて反復子本体の外に伝ぱ(播)された場合,列挙子
オブジェクトの状態は列挙後に変更されて,例外がDisposeメソッドの呼出し元に伝ぱ(播)さ
れる。
・ 状態を列挙後に変更する。
− 列挙子オブジェクトの状態が列挙後の場合,Disposeを呼び出しても何も起こらない。
462
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
26.3 列挙可能オブジェクト
列挙可能インタフェースを返す関数メンバが,反復子ブロックを使用して実装されている場合,その関
数メンバを呼び出すとすぐに反復子ブロックのコードが実行されるわけではない。実際には,列挙可能オ
ブジェクトが作成されて返される。列挙可能オブジェクトのGetEnumeratorメソッドは,反復子ブロッ
クに記述されたコードをカプセル化する列挙子オブジェクトを返し,その反復子ブロックのコードは,返
された列挙子オブジェクトのMoveNextメソッドが呼び出されたときに実行される。列挙可能オブジェク
トは次のような特徴をもつ。
− IEnumerable及びIEnumerator<T>を実装する。Tは反復子ブロックの産出型である。
− 関数メンバに渡される実引数の値のコピー(もしあれば)及びインスタンス値で初期化される。
列挙可能オブジェクトは通常,反復子ブロックのコードをカプセル化して列挙可能インタフェースを実
装した,コンパイラによって生成される列挙可能クラスのインスタンスであるが,他の実装方法も可能で
ある。列挙可能クラスがコンパイラによって生成される場合,そのクラスは直接的又は間接的に関数メン
バを含むクラスに入れ子になり,privateのアクセス可能性をもち,コンパイラが使用するために予約さ
れた名前(9.4.2参照)をもつ。
注記 列挙可能オブジェクトは,上記以外のインタフェースも実装可能である。特に,列挙可能オブ
ジェクトはIEnumerator及びIEnumerator<T>を実装して,列挙可能オブジェクト及び列
挙子オブジェクトの両方として機能することも可能である。このように実装した場合,最初に
列挙可能オブジェクトのGetEnumeratorメソッドが呼び出されると,その列挙可能オブジェ
クト自身が返される。その後に列挙可能オブジェクトのGetEnumeratorが呼び出された場合,
列挙可能オブジェクトのコピーが返される。したがって,返される列挙子オブジェクトはそれ
ぞれ別の状態をもち,ある列挙子オブジェクトの状態を変更しても他の列挙子オブジェクトに
は影響を与えない。
26.3.1 GetEnumeratorメソッド
列挙可能オブジェクトは,IEnumerableインタフェース及びIEnumerable<T>インタフェースの
GetEnumeratorメソッドの実装を提供する。二つのGetEnumeratorメソッドは,使用可能な列挙子オ
ブジェクトを取得して返すという共通の実装を共有する。列挙子オブジェクトは,列挙可能オブジェクト
が初期化されたときに保存された実引数の値及びインスタンス値で初期化されるが,それ以外においては
列挙子オブジェクトは26.2に述べられているように機能する。
26.4 実装例
注記 ここでは,標準のC#構築要素を使用した反復子の実装例を示す。ここで示す実装は,必ずこの
ように実装しなければならないものでもなく,唯一可能な実装でもない。
次に示すStack<T>クラスは,反復子を使用してGetEnumeratorメソッドを実装している。
この反復子は,スタックの要素を上から下に順番に列挙する。
using System;
using System.Collections;
using System.Collections.Generic;
class Stack<T>: IEnumerable<T>
{
T[] items;
int count;
463
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public void Push(T item) {
if (items == null) {
items = new T[4];
}
else if (items.Length == count) {
T[] newItems = new T[count * 2];
Array.Copy(items, 0, newItems, 0, count);
items = newItems;
}
items[count++] = item;
}
public T Pop() {
T result = items[--count];
items[count] = default(T);
return result;
}
public IEnumerator<T> GetEnumerator() {
for (int i = count - 1; i >= 0; --i) yield return items[i];
}
}
GetEnumeratorメソッドは次に示すように,反復子ブロックのコードをカプセル化する,
コンパイラによって生成される列挙子クラスのインスタンスに変形できる。
class Stack<T>: IEnumerable<T>
{
…
public IEnumerator<T> GetEnumerator() {
return new ̲̲Enumerator1(this);
}
class ̲̲Enumerator1: IEnumerator<T>, IEnumerator
{
int ̲̲state;
T ̲̲current;
Stack<T> ̲̲this;
int i;
public ̲̲Enumerator1(Stack<T> ̲̲this) {
this.̲̲this = ̲̲this;
}
public T Current {
get { return ̲̲current; }
}
464
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
object IEnumerator.Current {
get { return ̲̲current; }
}
public bool MoveNext() {
switch (̲̲state) {
case 1: goto ̲̲state1;
case 2: goto ̲̲state2;
}
i = ̲̲this.count - 1;
̲̲loop:
if (i < 0) goto ̲̲state2;
̲̲current = ̲̲this.items[i];
̲̲state = 1;
return true;
̲̲state1:
--i;
goto ̲̲loop;
̲̲state2:
̲̲state = 2;
return false;
}
public void Dispose() {
̲̲state = 2;
}
void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}
この変形では,反復子ブロックのコードが状態機械へと変形されて,列挙子クラスの
MoveNextメソッド内に配置されている。さらに,局所変数だったiは列挙子オブジェクトの
フィールドに変更され,複数回のMoveNextの呼出しにわたって存在可能になっている。
次の例では,1から10までの整数の簡単な演算表を出力する。この例のFromToメソッドは
列挙可能オブジェクトを返し,反復子を使用して実装されている。
using System;
using System.Collections.Generic;
class Test
{
static IEnumerable<int> FromTo(int from, int to) {
while (from <= to) yield return from++;
465
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
static void Main() {
IEnumerable<int> e = FromTo(1, 10);
foreach (int x in e) {
foreach (int y in e) {
Console.Write("{0,3} ", x * y);
}
Console.WriteLine();
}
}
}
FromToメソッドは次に示すように,反復子ブロックのコードをカプセル化した,コンパイ
ラによって生成される列挙可能クラスのインスタンスに変形できる。
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
class Test
{
…
static IEnumerable<int> FromTo(int from, int to) {
return new ̲̲Enumerable1(from, to);
}
class ̲̲Enumerable1:
IEnumerable<int>, IEnumerable,
IEnumerator<int>, IEnumerator
{
int ̲̲state;
int ̲̲current;
int ̲̲from;
int from;
int to;
int i;
public ̲̲Enumerable1(int ̲̲from, int to) {
this.̲̲from = ̲̲from;
this.to = to;
}
public IEnumerator<int> GetEnumerator() {
̲̲Enumerable1 result = this;
if (Interlocked.CompareExchange(ref ̲̲state, 1, 0) !=
466
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
0) {
result = new ̲̲Enumerable1(̲̲from, to);
result.̲̲state = 1;
}
result.from = result.̲̲from;
return result;
}
IEnumerator IEnumerable.GetEnumerator() {
return (IEnumerator)GetEnumerator();
}
public int Current {
get { return ̲̲current; }
}
object IEnumerator.Current {
get { return ̲̲current; }
}
public bool MoveNext() {
switch (̲̲state) {
case 1:
if (from > to) goto case 2;
̲̲current = from++;
̲̲state = 1;
return true;
case 2:
̲̲state = 2;
return false;
default:
throw new InvalidOperationException();
}
}
public void Dispose() {
̲̲state = 2;
}
void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}
列挙可能クラスは列挙可能インタフェース及び列挙子インタフェースを実装しているので,
列挙可能オブジェクト及び列挙子オブジェクトの両方として機能することが可能である。最初
467
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
にGetEnumeratorメソッドが呼び出されると,列挙可能オブジェクト自身が返される。その
後に列挙可能オブジェクトのGetEnumeratorが呼び出された場合,列挙可能オブジェクトの
コピーが返される。したがって,返される列挙子オブジェクトはそれぞれ別の状態をもち,あ
る列挙子オブジェクトの状態を変更しても他の列挙子オブジェクトには影響を与えない。
Interlocked.CompareExchangeメソッドはスレッド安全な操作を保証するために使用
される。
from仮引数及びto仮引数は,列挙可能クラスのフィールドに変更されている。fromの値
は反復子ブロックで変更されるので,それぞれの列挙子オブジェクトでfromに与えられる初
期値を保存するために̲̲fromフィールドが追加されている。
MoveNext
メソッドは,̲̲state
が
0
のときに呼び出されると
InvalidOperationExceptionを送出する。これによって,GetEnumeratorが呼び出され
る前に列挙可能オブジェクトが列挙子オブジェクトとして使用されることを防いでいる。
次の例では,簡単な木構造のクラスを示す。Tree<T>クラスは,反復子を使用して
GetEnumeratorメソッドを実装している。この反復子は,木構造の要素を挿入順に列挙する。
using System;
using System.Collections.Generic;
class Tree<T>: IEnumerable<T>
{
T value;
Tree<T> left;
Tree<T> right;
public Tree(T value, Tree<T> left, Tree<T> right) {
this.value = value;
this.left = left;
this.right = right;
}
public IEnumerator<T> GetEnumerator() {
if (left != null) foreach (T x in left) yield return x;
yield return value;
if (right != null) foreach (T x in right) yield return x;
}
}
class Program
{
static Tree<T> MakeTree<T>(T[] items, int left, int right) {
if (left > right) return null;
int i = (left + right) / 2;
return new Tree<T>(items[i],
MakeTree(items, left, i - 1),
MakeTree(items, i + 1, right));
468
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
static Tree<T> MakeTree<T>(params T[] items) {
return MakeTree(items, 0, items.Length - 1);
}
// The output of the program is:
// 1 2 3 4 5 6 7 8 9
// Mon Tue Wed Thu Fri Sat Sun
//
static void Main() {
Tree<int> ints = MakeTree(1, 2, 3, 4, 5, 6, 7, 8, 9);
foreach (int i in ints) Console.Write("{0} ", i);
Console.WriteLine();
Tree<string> strings = MakeTree("Mon", "Tue", "Wed", "Thu",
"Fri", "Sat", "Sun");
foreach (string s in strings) Console.Write("{0} ", s);
Console.WriteLine();
}
}
GetEnumeratorメソッドは次に示すように,反復子ブロックのコードをカプセル化した,
コンパイラによって生成される列挙子クラスのインスタンスに変形できる。
class Tree<T>: IEnumerable<T>
{
…
public IEnumerator<T> GetEnumerator() {
return new ̲̲Enumerator1(this);
}
sealed class ̲̲Enumerator1 : IEnumerator<T>, IEnumerator
{
Node<T> ̲̲this;
IEnumerator<T> ̲̲left, ̲̲right;
int ̲̲state;
T ̲̲current;
public ̲̲Enumerator1(Node<T> ̲̲this) { this.̲̲this = ̲̲this; }
public T Current { get { return ̲̲current; } }
public bool MoveNext() {
try {
switch (̲̲state) {
case 0:
̲̲state = -1;
if (̲̲this.left == null) goto ̲̲yield̲value;
469
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
̲̲left = ̲̲this.left.GetEnumerator();
goto case 1;
case 1:
̲̲state = -2;
if (!̲̲left.MoveNext()) goto
̲̲left̲dispose;
̲̲current = ̲̲left.Current;
̲̲state = 1;
return true;
̲̲left̲dispose:
̲̲state = -1;
̲̲left.Dispose();
̲̲yield̲value:
̲̲current = ̲̲this.value;
̲̲state = 2;
return true;
case 2:
̲̲state = -1;
if (̲̲this.right == null) goto ̲̲end;
̲̲right = ̲̲this.right.GetEnumerator();
goto case 3;
case 3:
̲̲state = -3;
if (!̲̲right.MoveNext()) goto
̲̲right̲dispose;
̲̲current = ̲̲right.Current;
̲̲state = 3;
return true;
̲̲right̲dispose:
̲̲state = -1;
̲̲right.Dispose();
̲̲end:
̲̲state = 4;
break;
}
} finally {
if (̲̲state < 0) Dispose();
}
return false;
}
470
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public void Dispose() {
try {
switch (̲̲state) {
case 1:
case -2:
̲̲left.Dispose();
break;
case 3:
case -3:
̲̲right.Dispose();
break;
}
} finally {
̲̲state = 4;
}
}
object IEnumerator.Current { get { return Current; } }
void IEnumerator.Reset() {
throw new NotSupportedException();
}
}
}
foreach文で使用されていた,コンパイラによって生成される一時変数は,列挙子オブジェ
クトの̲̲left及び̲̲rightフィールドに変更されている。列挙子オブジェクトの̲̲state
フィールドは,例外が送出された場合も正しいDispose()メソッドが正しく呼び出されるよ
うに注意深く更新されている。変形したコードは簡単なforeach文を使用して記述できない
ことに注意する。
27 安全でないコード
安全でないコードを提供しない実装は,キーワードunsafeが使用されていないかどうかの診断メッセ
ージの出力を要求される。
この箇条は,すべての節を含め,条件付き規定とする。
注記 箇条1〜箇条26で定義されたC#言語の中核機能は,データ型としてのポインタを省略した点
で,C及びC++とは大きく異なる。代わりに,C#には,参照及びゴミ集め子によって管理され
るオブジェクトを生成する機能がある。この設計及び他の機能を組み合わせることによって,
C#は,C又はC++よりもはるかに安全な言語になっている。C#言語の中核機能では,初期化さ
れていない変数,参照先がないポインタ,又は,配列の範囲を越えて添字指定する式を使用で
きない。このため,C及びC++のプログラムに発生しがちな,すべての種類のバグが排除され
る。
C又はC++におけるほぼすべてのポインタ型の構築要素には,対応する参照型がC#に存在す
471
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
る。しかし,C#でもポインタ型へのアクセスが必要になる状況がある。例えば,基礎となるオ
ペレーティングシステムとのインタフェース,メモリに割り当てられた装置へのアクセス,又
は,時間に厳しい制約があるアルゴリズムの実装を行う場合は,ポインタへのアクセスなしで
は,不可能か,又は,実用的でない。この必要性に対処するために,C#には安全でないコード
を記述する機能が用意されている。
安全でないコードでは,ポインタについての宣言及び操作,ポインタと整数型との間の変換,
変数のアドレスの取得などが可能になる。安全でないコードを記述することは,ある意味で,
C#プログラム内にCコードを記述するのと似ている。
実際には,安全でないコードは,開発者及び利用者の両方の立場からは,“安全”な機能とい
える。安全でないコードは,修飾子unsafeを用いて明確に指定されなければならない。その
ために,開発者は偶然的に安全ではない機能を使用できないし,実行エンジンは,安全でない
コードが,信頼できない環境において実行できないことを保証するように動作する。
27.1 安全でない文脈
C#の安全でない機能は,安全でない文脈だけで利用できる。安全でない文脈は,型又はメンバの宣言の
中にunsafe修飾子を含むことによって,又は,≪安全でない文≫を使用することによって,導入される。
− クラス,構造体,インタフェース又は委譲の宣言は,unsafe修飾子を含んでもよく,その場合,(ク
ラス,構造体又はインタフェースの本体を含む)型宣言の記述上の範囲の全体は,安全でない文脈と
みなされる。
注記 型宣言が部分的である場合,その部分だけが安全でない文脈である。
− フィールド,メソッド,特性,イベント,添字子,演算子,インスタンス構築子,終了化子又は静的
構築子の宣言は,unsafe修飾子を含んでよい。この場合,そのメンバ宣言の記述上の範囲全体が,
安全でない文脈とみなされる。
− ≪安全でない文≫を使用すると,≪ブロック≫内で安全でない文脈を使用できる。関連する≪ブロッ
ク≫の記述範囲全体が,安全でない文脈とみなされる。
関連する文法の拡張を次に示す。表記を簡略にするために,省略記号(...)を用いて,以前の箇条で示
された生成規則を表現する。
≪クラス修飾子≫:
...
unsafe
≪構造体修飾子≫:
...
unsafe
≪インタフェース修飾子≫:
...
unsafe
≪委譲修飾子≫:
...
unsafe
≪フィールド修飾子≫:
...
472
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
unsafe
≪メソッド修飾子≫:
...
unsafe
≪特性修飾子≫:
...
unsafe
≪イベント修飾子≫:
...
unsafe
≪添字子修飾子≫:
...
unsafe
≪演算子修飾子≫:
...
unsafe
≪構築子修飾子≫:
...
unsafe
≪終了化子宣言≫:
≪属性≫opt externopt unsafeopt ~ ≪識別子≫ ( ) ≪終了化子本体≫
≪属性≫opt unsafeopt externopt ~ ≪識別子≫ ( ) ≪終了化子本体≫
≪静的構築子修飾子群≫:
externopt unsafeopt static
unsafeopt externopt static
externopt static unsafeopt
unsafeopt static externopt
static externopt unsafeopt
static unsafeopt externopt
≪埋込み文≫:
...
≪安全でない文≫
≪安全でない文≫:
unsafe≪ブロック≫
例
public unsafe struct Node
{
public int Value;
public Node* Left;
public Node* Right;
473
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
この例では,構造体宣言内で指定されるunsafe修飾子によって構造体宣言のすべての記述範
囲がunsafe文脈になる。Leftフィールド及びRightフィールドは,ポインタ型と宣言するこ
とが可能となる。上の例は次のようにも記述できる。
public struct Node
{
public int Value;
public unsafe Node* Left;
public unsafe Node* Right;
}
フィールド宣言内のunsafe修飾子は,その宣言を安全でない文脈にあるとする。
unsafe修飾子は,ポインタ型の使用を許可する安全でない文脈を設定する以外には,型及びメンバに
影響を及ぼさない。
例
public class A
{
public unsafe virtual void F() {
char* p;
…
}
}
public class B: A
{
public override void F() {
base.F();
…
}
}
A内のメソッドFのunsafe修飾子は,Fの記述範囲を安全でない文脈にし,C#言語の安全で
ない機能を使用可能にする。B内のFの上書きでは,unsafe修飾子を再度指定する必要はない
が,Bそのものの中でメソッドFが安全でない機能を使うならば再指定の必要がある。
ポインタ型がメソッドの呼出し情報に含まれる場合は,これとは状況が少し異なる。
public unsafe class A
{
public virtual void F(char* p) {…}
}
public class B: A
{
public unsafe override void F(char* p) {…}
}
474
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
この例は,Fの呼出し情報にポインタ型が含まれるため,安全でない文脈においてだけ記述で
きる。安全でない文脈は,Aで示すようにクラス全体を安全でないとするか,又は,Bで示すよ
うにメソッド宣言中に修飾子unsafeを含むことによって導入される。
unsafe修飾子は,部分型宣言 (17.1.4) 内で使用された場合,その特定の部分が,安全でない
文脈と考えられる。
27.2 ポインタ型
安全でない文脈では,≪型≫(箇条11参照)は≪ポインタ型≫,≪値型≫,≪参照型≫又は≪型仮引数
≫でなければならない。
≪型≫:
≪値型≫
≪参照型≫
≪型仮引数≫
≪ポインタ型≫
ポインタ型は,非管理型又はキーワードvoidの後に字句*が続くことによって記述される。
≪ポインタ型≫:
≪非管理型≫ *
void *
≪非管理型≫:
≪型≫
ポインタ型で*の前に指定された型は,ポインタ型の参照先型 (referent type) と呼ばれる。これは,ポイ
ンタ型の値が指す変数の型を表現する。
参照型の値である参照とは異なり,ポインタはゴミ集め子によって追跡されない。ゴミ集め子は,ポイ
ンタ及びポインタが指すデータについての知識をもたない。このため,ポインタは,参照又は参照を含む
構造体を指すことを許されない。ポインタの参照先型は≪非管理型≫でなければならない。
≪非管理型≫は,≪参照型≫でも,≪型仮引数≫でも,総称型の≪構造体型≫でもない型とする。≪非
管理型≫はすべてのフィールドが≪非管理型≫でなければならない。すなわち,≪非管理型≫は次のいず
れかとなる。
− sbyte,byte,short,ushort,int,uint,long,ulong,char,float,double,decimal
又はbool。
− 任意の≪列挙型≫。
− 任意の≪ポインタ型≫。
− ≪非管理型≫のフィールドだけを格納する,総称型でない,利用者定義の≪構造体型≫。
注記 構築型と≪型仮引数≫は≪非管理型≫ではない。
ポインタと参照を混在させる場合の直感的な規則は,参照の参照先(オブジェクト)ではポインタを含
めることができても,ポインタの参照先では参照を含めることができないことにある。
例 ポインタ型の例を幾つか次の表に示す。
例
説明
byte*
byteへのポインタ
char*
charへのポインタ
475
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int**
intへのポインタへのポインタ
int[]*
intへのポインタの一次元配列
void*
未知の型へのポインタ
適合実装では,すべてのポインタ型が同じ大きさ及び表現をもつ必要がある。
注記 C及びC++とは異なり,複数のポインタを同じ宣言内で宣言する場合,C#での*は,各ポイン
タ名の接頭辞区切り子としてではなく,基礎となる参照先型にくっつけて記述される。次に例
を示す。
int* pi, pj; // NOT as int *pi, *pj;
型T*をもつポインタの値は型Tをもつ変数のアドレスを表す。ポインタ間接参照演算子*(27.5.1参照)
は,この変数へのアクセスに使用できる。
例 例えば,int*型をもつ変数Pにおいて,式*Pは,Pに含まれるアドレスに存在するint型変数
を表す。
オブジェクト参照同様,ポインタはnullであってよい。nullポインタに対する間接参照演算の振る
舞いは,実装定義となる。nullポインタの値は,すべてのビットがゼロで表される。
void*型は,未知の型へのポインタを表す。参照されている型が不明なので,間接参照演算子は,型
void*のポインタへ適用できない,また,そのようなポインタには算術演算を適用できない。しかし,void*
型のポインタを他のポインタ型へキャストできるし,その逆も可能であり,また,他のポインタ型の値と
比較可能である。
ポインタ型は,他の型とは独立した種類となる。参照型及び値型とは異なり,ポインタ型はobjectを
継承しないので,ポインタ型とobjectとの間の型変換はできない。特にボックス化及びボックス化解除
(11.3参照)はポインタ型に対しては提供されない。ただし,異なるポインタ型の間の変換及びポインタ
型と整数型との間の変換は可能とする。これは27.4で規定される。
≪ポインタ型≫はvolatileフィールドの型として使用できる(17.4.3参照)。
注記 ポインタは,ref及びout仮引数に渡すことができるが,そうすると振る舞いが未定義となる。
なぜならポインタは局所変数を指すように設定できるので,呼び出されたメソッドから戻った
とき局所変数がもはや存在しない,又は,指していた固定オブジェクトが固定でなくなる可能
性をもつからである。次に例を示す。
using System;
class Test
{
static int value = 20;
unsafe static void F(out int* pi1, ref int* pi2) {
int i = 10;
pi1 = &i;
fixed (int* pj = &value) {
// ...
pi2 = pj;
}
}
476
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
static void Main() {
int i = 10;
unsafe {
int* px1;
int* px2 = &i;
F(out px1, ref px2);
Console.WriteLine("*px1 = {0}, *px2 = {1}",
*px1, *px2); // undefined behavior
}
}
}
メソッドは,何らかの型の値を返すことができるので,その型がポインタ型であってもよい。
例 例えば,intの連続列へのポインタ,列の要素数及びint値が与えられたとき,次のメソッドは,
その値に合致した列内の値があれば,そのアドレスを返し,なければnullを返す。
unsafe static int* Find(int* pi, int size, int value) {
for (int i = 0; i < size; ++i) {
if (*pi == value) {
return pi;
}
++pi;
}
return null;
}
安全でない文脈では,ポインタの操作として次を利用できる。
− 単項演算子*は,ポインタ間接参照のために使用できる(27.5.1参照)。
− 演算子->は,ポインタを通して構造体のメンバにアクセスするために使用できる(27.5.2参照)。
− 演算子[]は,ポインタを添字指定するために使用できる(27.5.3参照)。
− 単項演算子&は,変数のアドレスを得るために使用できる(27.5.4参照)。
− 演算子++及び--は,ポインタを増加,減少させるために使用できる(27.5.5参照)。
− 二項演算子+及び-は,ポインタ算術演算を実行するために使用できる(27.5.6参照)。
− 演算子==,!=,<,>,<=及び=>は,ポインタ比較のために使用できる(27.5.7参照)。
− 演算子stackallocは,呼出しスタックからメモリを割り当てるために使用できる(27.7参照)。
− fixed文は,変数のアドレスを得ることができるよう変数を一時的に固定するために使用できる(27.6
参照)。
27.3 固定変数及び移動可能変数
アドレス参照演算子(27.5.4参照)及びfixed文(27.6参照)は,変数を固定変数及び移動可能変数の
二つの部類に分ける。
固定変数は,ゴミ集め子の処理の影響を受けない記憶領域に存在する(固定変数の例には,局所変数,
値仮引数及び参照がたぐられたポインタによって生成された変数が含まれる。)。他方,移動可能変数は,
ゴミ集め子による再割当て及び破棄の対象となる記憶領域に存在する(移動可能変数の例には,オブジェ
477
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
クト内のフィールド及び配列要素が含まれる。)。
演算子&(27.5.4参照)は,固定変数のアドレスを制限なく取ることができる。しかし,移動可能変数は,
ゴミ集め子による再割当て及び破棄の対象になるので,fixed文(27.6参照)を用いてだけ移動可能変数
のアドレスを得ることができる。また,そのアドレスはfixed文の有効期間だけ有効となる。
厳密には,固定変数は,次のいずれかになる。
− 局所変数又は値仮引数を参照する≪単純名≫(14.5.2参照)による変数であって,その変数が無名メ
ソッド (14.5.15.3.1) によって拘束されていない場合に限る。
− Vが≪構造体型≫の固定変数であるとき,形式V.Iの≪メンバアクセス≫(14.5.4参照)による変数。
− 形式*Pの≪ポインタ間接参照式≫(27.5.1参照),形式P->Iの≪ポインタメンバアクセス≫(27.5.2
参照)又は形式P[E]の≪ポインタ要素アクセス≫(27.5.3参照)による変数。
これ以外のすべての変数は,移動可能変数に分類される。
静的フィールドは,移動可能変数に分類される。ref仮引数及びout仮引数は,仮引数に対する実引数
が固定変数であっても移動可能変数に分類される。ポインタの参照をたぐられて生成される変数は,常に
固定変数に分類される。
27.4 ポインタ変数
安全でない文脈では,利用可能な暗黙の型変換(13.1参照)の集合は,次の暗黙のポインタ型変換を含
むように拡張される。
− 任意の≪ポインタ型≫から型void*への変換。
− null型から任意の≪ポインタ型≫への変換。
さらに,安全でない文脈では,利用可能な明示的型変換(13.2参照)の集合は,次の明示的ポインタ型
変換を含むように拡張される。
− 任意の≪ポインタ型≫から他の≪ポインタ型≫への変換。
− sbyte,byte,short,ushort,int,uint,long又はulongから任意の≪ポインタ型≫への変
換。
− 任意の≪ポインタ型≫からsbyte,byte,short,ushort,int,uint,long又はulongへの
変換。
最後に,安全でない文脈では,標準暗黙型変換(13.3.1参照)の集合に,次のポインタ型変換を含む。
− 任意の≪ポインタ型≫からvoid*型への変換。
二つのポインタ型間の変換によって,実際のポインタ値が変更されることはない。すなわち,あるポイ
ンタ型から別のポインタ型に変換しても,ポインタによって指定された基礎となるアドレスに影響はない。
あるポインタ型を別のポインタ型に変換したときに,結果として得られたポインタがその型に正しく位
置合わせされていない場合には,そのポインタの参照をたぐったときの振る舞いは定義されない。一般的
に,”正しい位置合わせ”という概念は,遷移的となる。型Aへのポインタが型Bへのポインタに対して正
しく位置合わせされていて,併せて,型Bへのポインタが型Cへのポインタに対して正しく位置合わせさ
れているときには,型Aへのポインタは型Cへのポインタに対して正しく位置合わせされている。
例 ある型をもつ変数に対して異なる型へのポインタを通してアクセスするという例を次に示す。
char c = 'A';
char* pc = &c;
void* pv = pc;
int* pi = (int*)pv;
478
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
int i = *pi;
// undefined
*pi = 123456;
// undefined
あるポインタ型が,バイトを指すポインタに変換された場合,結果は変数のアドレスの最下位バイトを
指す。結果を変数の大きさだけ連続して増加すると,その変数の残りのバイトへのポインタがそれぞれ生
成される。
例 例えば,次のメソッドは,double型の8バイトをそれぞれ16進数値として表示する。
using System;
class Test
{
static void Main() {
double d = 123.456e23;
unsafe {
byte* pb = (byte*)&d;
for (int i = 0; i < sizeof(double); ++i)
Console.Write(" {0,2:X}", (uint)(*pb++));
Console.WriteLine();
}
}
}
もちろん出力は,バイト順序に依存する。
ポインタと整数との間の対応付けは,実装定義とする。
注記 しかし線形アドレス空間をもつ32ビット又は64ビットアーキテクチャのCPUでは,ポインタ
と整数型との間の変換は,それぞれuint又はulongの値と整数型との間の変換と全く同様に
振る舞う。
27.5 式中のポインタ
安全でない文脈では,式がポインタ型の結果を生成することがある。安全でない文脈の外では,式がポ
インタ型を生成するとコンパイル時エラーになる。厳密には,安全でない文脈の外では,任意の≪単純名
≫(14.5.2参照),≪メンバアクセス≫(14.5.4参照),≪呼出し式≫(14.5.5参照)又は≪要素アクセス≫
(14.5.6参照)がポインタ型であるならば,コンパイル時エラーになる。
≪一次非配列生成式≫(14.5参照)生成規則に,次の付加的な書き方を許す。
≪一次非配列生成式≫:
...
≪sizeof式≫
安全でない文脈では,≪一次非配列生成式≫(14.5参照)及び≪単項式≫(14.6参照)の生成規則は,
次の要素の追加を許す。
≪一次非配列生成式≫:
...
479
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪ポインタメンバアクセス≫
≪ポインタ要素アクセス≫
≪単項式≫:
...
≪ポインタ間接参照式≫
≪アドレス参照式≫
これらの構築要素については,25.5.1〜25.5.7で規定する。
ポインタに対しては,複数のあらかじめ定義された単項演算子及び二項演算子がある。単項演算子及び
二項演算子を演算子の多重定義解決に際して,実際の演算対象がいずれもポインタ型でない場合は,ポイ
ンタ仮引数型を伴うあらかじめ定義された単項演算子及び二項演算子は考慮しない。
注記 安全でない演算子の優先順位及び結合規則は,構文文法によって暗黙に定義される。
27.5.1 ポインタ間接参照
≪ポインタ間接参照式≫は,星印(*)の後ろに≪単項式≫が続く。
≪ポインタ間接参照式≫:
* ≪単項式≫
単項演算子*は,ポインタ間接参照を表し,ポインタが指す変数を取得するために使用される。*Pの評
価結果は,Pがポインタ型T*の式であるとき,型Tの変数となる。単項演算子*がvoid*型の式に適用さ
れた場合又はポインタ型でない式に適用された場合は,コンパイル時エラーになる。
単項演算子*が,ポインタnullに適用された場合の効果は,実装定義とする。特に,この操作が
System.NullReferenceExceptionを送出することを保証しないことに注意する。
ポインタに不当な値が設定されている場合,単項演算子*の振る舞いは未定義とする。
注記 この単項*演算子によるポインタの参照をたぐることに対する不当な値の中には,指している
型に対して位置合わせが不適正なアドレス(27.4の例を参照)又は有効期間が終わった後の変
数のアドレスがある。
確実な代入の分析では,形式*Pの式を評価して生成される変数は,初期化時に代入されているものとみ
なす。
27.5.2 ポインタメンバアクセス
≪ポインタメンバアクセス≫は,≪一次式≫,字句“->”,更に≪識別子≫からなる。
≪ポインタメンバアクセス≫:
≪一次式≫ -> ≪識別子≫ ≪型実引数並び≫opt
形式P->Iのポインタメンバアクセスにおいて,Pはvoid*以外のポインタ型の式でなければならず,
IはPが指す型のアクセス可能なメンバを表現しなければならない。
形式P->Iのポインタメンバアクセスは,正確には(*P).Iとして評価される。ポインタ間接演算子(*)
の規定は,27.5.1に示す。メンバアクセス演算子(.)の規定は,14.5.4を参照。
例
struct Point
{
public int x;
public int y;
480
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public override string ToString() {
return "(" + x + "," + y + ")";
}
}
using System;
class Test
{
static void Main() {
Point point;
unsafe {
Point* p = &point;
p->x = 10;
p->y = 20;
Console.WriteLine(p->ToString());
}
}
}
この例では,->演算子を用いて,ポインタによるフィールドへのアクセス及び構造体のメソッ
ドの呼出しが行われる。演算P->Iは,(*P)と正確に等値なので,メソッドMainは,次のよう
にも記述できる。
using System;
class Test
{
static void Main() {
Point point;
unsafe {
Point* p = &point;
(*p).x = 10;
(*p).y = 20;
Console.WriteLine((*p).ToString());
}
}
}
27.5.3 ポインタ要素アクセス
≪ポインタ要素アクセス≫は,≪一次非配列生成式≫の後に“[”と“]”で囲まれた式が続く。
≪ポインタ要素アクセス≫:
≪一次非配列生成式≫ [ ≪式≫ ]
形式P[E]のポインタ要素アクセスにおいては,Pがvoid*以外のポインタ型の式でなければならず,E
481
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
が暗黙にint,uint,long又はulongへ変換できる型の式でなければならない。
形式P[E]のポインタ要素アクセスは,*(P + E)として評価される。ポインタ間接演算子(*)の規定は,
27.5.1を参照し,ポインタ加算演算子(+)の規定は,27.5.6を参照。
例
class Test
{
static void Main() {
unsafe {
char* p = stackalloc char[256];
for (int i = 0; i < 256; i++) p[i] = (char)i;
}
}
}
ポインタ要素アクセスが,forループ内で文字バッファを初期化するために使用されている。
処理P[E]は,*(P + E)と正確に等しいので,次のように書いても同じである。
class Test
{
static void Main() {
unsafe {
char* p = stackalloc char[256];
for (int i = 0; i < 256; i++) *(p + i) = (char)i;
}
}
}
ポインタ要素アクセス演算子は,範囲外エラーを検査しない。また,範囲外の要素にアクセスした場合
の振る舞いは未定義とする。
注記 これは,C及びC++と同じである。
27.5.4 アドレス参照演算子
≪アドレス参照式≫は,&の後ろに≪単項式≫が続く。
≪アドレス参照式≫:
& ≪単項式≫
型Tをもち,固定変数(27.3参照)として分類される式Eが与えられたとき,&EはEによって与えら
れる変数のアドレスを計算する。結果の型はT*であり値に分類される。Eが変数に分類されない場合,E
が読込み専用変数の場合,又は,Eが移動可能変数を表記する場合には,コンパイル時エラーになる。移
動可能変数の場合,変数のアドレスを得る前に変数を一時的に“固定”するためにfixed文(27.6参照)
が使用できる。
&演算子では,その実引数が確実に代入されている必要はない。しかし,&演算の後では,&演算子が適
用された変数は,演算が発生する実行パスで確実に代入されているとみなされる。プログラマは,この状
況で変数の適切な初期化が確かに行われる責任をもつ。
例
482
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
using System;
class Test
{
static void Main() {
int i;
unsafe {
int* p = &i;
*p = 123;
}
Console.WriteLine(i);
}
}
この例では,pを初期化するために使用される&i処理の後でiは確実に代入されたと考えられ
る。実際*pへの代入はiを初期化するが,この初期化を含めることはプログラマの責任である。
この代入が削除されてもコンパイル時エラーにはならない。
注記1 &演算子に対するこのような確実な代入の規則が存在するのは,局所変数の冗長な初期化を
回避するためである。例えば,外部APIの多くは,そのAPIによって値が埋められる構造体
へのポインタを使用する。そのようなAPIの典型的な呼出しでは,局所構造体変数のアドレ
スを渡す。このような規則がないと,構造体変数を冗長に初期化する必要が生じる。
注記2 14.5.4において規定したように,読込み専用フィールドを定義する構造体又はクラスに対す
るインスタンス構築子又は静的構築子の外側では,そのフィールドは値と考えられて,変数
とはみなされない。したがって,この場合,そのアドレスは取得できない。同様に,定数の
アドレスも取れない。
局所変数,値仮引数又は仮引数配列が,無名メソッド(14.5.15.3.1参照)によって拘束された場合,そ
の局所変数,値仮引数又は仮引数配列は固定変数(27.3参照)ではなく,移動可能変数とする。したがっ
て,無名メソッドによって拘束された局所変数,値仮引数又は仮引数配列に対してアドレスを取得する安
全でないプログラムはエラーとなる。
27.5.5 ポインタ加算及び減算
安全でない文脈では,演算子++及び--(14.5.9及び14.6.5参照)をvoid*以外のすべての型のポインタ
変数に適用できる。
T* operator ++(T* x);
T* operator --(T* x);
これらの演算子は,x+1及びx-1の場合と同じ結果を生成する(27.5.6参照)。言い換えると,型T*の
ポインタ変数に対して,++演算子は,変数に含まれるアドレスにsizeof(T)を加算し,--演算子は,変
数に含まれるアドレスからsizeof(T)を減算する。
ポインタ加算演算又はポインタ減少演算がポインタ型の領域をけた(桁)あふれした場合には,その結
果は実装定義とするが,例外は生成されない。
27.5.6 ポインタ算術
安全でない文脈では+演算子(14.7.4参照)及び‒演算子(14.7.5参照)は,void*以外のすべてのポイ
483
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
ンタ型の値に適用できる。すべてのポインタ型T*に関して,次の演算子が暗黙に定義されている。
T* operator +(T* x, int y);
T* operator +(T* x, uint y);
T* operator +(T* x, long y);
T* operator +(T* x, ulong y);
T* operator +(int x, T* y);
T* operator +(uint x, T* y);
T* operator +(long x, T* y);
T* operator +(ulong x, T* y);
T* operator ‒(T* x, int y);
T* operator ‒(T* x, uint y);
T* operator ‒(T* x, long y);
T* operator ‒(T* x, ulong y);
long operator ‒(T* x, T* y);
ポインタ型T*の式P及び型int,uint,long又はulongの式Nが与えられたとき,式P + N及び
式N + Pは,T*型のポインタ値を計算し,その結果は,Pによって与えられるアドレスにN * sizeof(T)
を加えた値になる。同様に,式P ‒ Nは,T*型のポインタ値を計算し,その結果は,Pによって与えら
れるアドレスからN * sizeof(T)を減算した値となる。
ポインタ型T*の二つの式,P及びQが与えられたとき,式P ‒ Qの結果は,PとQとによって与えら
れるアドレスの差をsizeof(T)で割算したものとなる。この結果の型は,常にlongとする。P - Qは,
実際((long)(P) - (long)(Q)) / sizeof(T)として計算される。
例
using System;
class Test
{
static void Main() {
unsafe {
int* values = stackalloc int[20];
int* p = &values[1];
int* q = &values[15];
Console.WriteLine("p - q = {0}", p - q);
Console.WriteLine("q - p = {0}", q - p);
}
}
}
この例は,次を出力する。
p - q = -14
q - p = 14
ポインタ算術演算がポインタ型の領域をけた(桁)あふれした場合,結果は実装で定義される方法で切
り捨てられ,例外は生成されない。
484
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
27.5.7 ポインタ比較
安全でない文脈では,演算子==,!=,<,>,<=,及び=>(14.9参照)は,すべてのポインタ型の値に
適用できる。ポインタ比較演算子は,次とする。
bool operator ==(void* x, void* y);
bool operator !=(void* x, void* y);
bool operator <(void* x, void* y);
bool operator >(void* x, void* y);
bool operator <=(void* x, void* y);
bool operator >=(void* x, void* y);
任意のポインタ型からvoid*型への暗黙の変換が存在するので,任意のポインタ型の演算対象をこれら
の演算子を用いて比較できる。比較演算子は,二つの演算対象で指定されるアドレスをあたかも符号なし
整数であるかのように比較する。
27.5.8 sizeof演算子
sizeof演算子は,指定された型の変数が占める8ビットのバイト数を返す。sizeof演算子の演算対
象として指定する型は,≪非管理型≫(27.2参照)でなければならない。
≪sizeof式≫:
sizeof ( ≪非管理型≫ )
14.5.12に指定されたあらかじめ定義された型に対して,sizeof演算子はint型の定数を生成する。≪
sizeof式≫は安全でない文脈の内側と外側の両方で使用できる。
その他のすべての型については,sizeof演算子の結果は,実装定義とし,定数ではなく値として分類
される。
メンバが一つの構造体にまとめられるときの順序は未規定とする。
境界の位置合わせのために,構造体の先頭,内部及び末尾に,名前のない詰物を置く場合がある。詰物
として使用されるビットの内容は不定とする。
構造体型をもつ演算対象にsizeof演算子を適用した場合,その結果は,その型の変数内の詰物を含め
た合計バイト数となる。
27.6 fixed文
安全でない文脈では,≪埋込み文≫(箇条15参照)の生成規則に,移動可能変数のアドレスが,その文
の有効期間に一定となるよう“固定”する追加要素としてfixed文が許される。
≪埋込み文≫:
...
≪固定文≫
≪固定文≫:
fixed ( ≪ポインタ型≫ ≪固定ポインタ宣言子群≫ ) ≪埋込み文≫
≪固定ポインタ宣言子群≫:
≪固定ポインタ宣言子≫
≪固定ポインタ宣言子群≫ , ≪固定ポインタ宣言子≫
≪固定ポインタ宣言子≫:
≪識別子≫ = ≪固定ポインタ初期化子≫
≪固定ポインタ初期化子≫:
485
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
& ≪変数参照≫
≪式≫
≪固定ポインタ宣言子≫は,与えられた≪ポインタ型≫の局所変数を宣言し,対応する≪固定ポインタ
初期化子≫によって計算されるアドレスで,その局所変数を初期化する。fixed文中で宣言された局所変
数は,その変数宣言の右側にあるすべての≪固定ポインタ初期化子≫群内でアクセス可能となる。fixed
文によって宣言された局所変数は,読込み専用とみなされる。埋込み文が,代入,++演算子又は--演算子
で局所変数を変更しようとしたり,局所変数をref仮引数又はout仮引数として渡そうとしたりすると,
コンパイル時エラーになる。
≪固定ポインタ初期化子≫で,局所変数(14.5.15.3.1参照),値仮引数又は,仮引数配列を使用し,かつ,
その変数が拘束されている場合,エラーとする(14.5.15.3.1参照)。
≪固定ポインタ初期化子≫には次のいずれかを指定できる。
− 型T*がfixed文で与えられたポインタ型に暗黙に互換な条件下で,字句“&”に非管理型Tの移動
可能変数(27.3参照)への≪変数参照≫(12.4参照)を続けたもの。この場合,初期化子は,指定さ
れた変数のアドレスを計算し,fixed文の有効期間において変数は固定されたアドレスのままとなる。
− 型T*がfixed文で与えられたポインタ型に暗黙に互換な条件下で,非管理型Tの要素をもつ≪配列
型≫の式。この場合,初期化子は,配列内の先頭要素のアドレスを計算し,fixed文の有効期間にお
いて,配列全体が固定されたアドレスにとどまる。配列式が空又は配列が要素数0の場合には,fixed
文の振る舞いは実装定義とする。
− 型T*がfixed文で与えられたポインタ型に暗黙に互換な条件下で,string型の式。この場合,初
期化子は,文字列内の先頭文字のアドレスを計算し,fixed文の有効期間において,文字列全体が固
定されたアドレスにとどまる。文字列式が空の場合,fixed文の振る舞いは実装定義とする。
≪固定ポインタ初期化子≫で計算された各アドレスについて,fixed文は,そのアドレスで参照される
変数が,fixed文の有効期間中は,ゴミ集め子で再配置又は廃棄されないことを保証する。
例 例えば,≪固定ポインタ初期化子≫によって計算されるアドレスがオブジェクトのフィールド又
は配列の要素を参照するならば,fixed文は,その文の有効期間の間は,オブジェクトインスタ
ンスが再割当てされたり廃棄されたりしないことを保証する。
プログラマには,fixed文で生成されたポインタが,文の実行終了後にまで残ってしまわないようにす
る責任がある。
例 例えば,fixed文によって生成されたポインタが外部APIに渡されたとき,APIがそのポインタ
を記憶しないことを保証するのはプログラマの責任となる。
固定されたオブジェクトによってヒープの断片化が発生することがある。これは,オブジェクトを移動
できないためである。このため,絶対に必要な場合にだけ,できるだけ短い時間に限って,オブジェクト
を固定するようにすることを推奨する。
例
class Test
{
static int x;
int y;
unsafe static void F(int* p) {
*p = 1;
486
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
static void Main() {
Test t = new Test();
int[] a = new int[10];
unsafe {
fixed (int* p = &x) F(p);
fixed (int* p = &t.y) F(p);
fixed (int* p = &a[0]) F(p);
fixed (int* p = a) F(p);
}
}
}
この例は,fixed文の使い方を幾つか示す。最初の文は,静的フィールドのアドレスを固定し,
取得する。2番目の文は,インスタンスフィールドのアドレスを固定取得する。3番目の文は,配
列要素のアドレスを固定取得する。いずれの場合も,通常の演算子&を使用したのでは,変数が
すべて移動可能変数として分類されるので,エラーになる。
上の例で,3番目及び4番目のfixed文は,同一の結果を与える。一般に,配列インスタンス
aについて,fixed文中の&a[0]は,単にaと指定することと同じとなる。
stringを使用したfixed文の別の例を次に示す。
class Test
{
static string name = "xx";
unsafe static void F(char* p) {
for (int i = 0; p[i] != '¥0'; ++i)
Console.WriteLine(p[i]);
}
static void Main() {
unsafe {
fixed (char* p = name) F(p);
fixed (char* p = "xx") F(p);
}
}
}
安全でない文脈では,1次元配列の配列要素は,添字0から始まりLength ‒ 1で終了する添字の昇順
で格納される。多次元配列では,配列要素は,最右次元の添字がまず最初に増やされ,次にその左の次元
の添字が増やされ,同様に左に移って増やされるように格納される。
fixed文内で配列aへのポインタpを得た場合,そのポインタ値はpからp + a.Length ‒ 1の範囲の
値をもち,配列内の要素のアドレスを表す。同様に,p[0]からp[a.Length - 1]までの範囲の変数は,
実際の配列要素を表す。配列の格納方法が与えられれば,任意の次元の配列をあたかも線形であるかのよ
うに処理できる。
487
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
例1
using System;
class Test
{
static void Main() {
int[,,] a = new int[2,3,4];
unsafe {
fixed (int* p = a) {
for (int i = 0; i < a.Length; ++i) // treat as linear
p[i] = i;
}
}
for (int i = 0; i < 2; ++i)
for (int j = 0; j < 3; ++j) {
for (int k = 0; k < 4; ++k)
Console.Write("[{0},{1},{2}] = {3,2} ", i,
j, k,
a[i,j,k]);
Console.WriteLine();
}
}
}
この例は,次を出力する。
[0,0,0] = 0 [0,0,1] = 1 [0,0,2] = 2 [0,0,3] = 3
[0,1,0] = 4 [0,1,1] = 5 [0,1,2] = 6 [0,1,3] = 7
[0,2,0] = 8 [0,2,1] = 9 [0,2,2] = 10 [0,2,3] = 11
[1,0,0] = 12 [1,0,1] = 13 [1,0,2] = 14 [1,0,3] = 15
[1,1,0] = 16 [1,1,1] = 17 [1,1,2] = 18 [1,1,3] = 19
[1,2,0] = 20 [1,2,1] = 21 [1,2,2] = 22 [1,2,3] = 23
例2
class Test
{
unsafe static void Fill(int* p, int count, int value) {
for (; count != 0; count--) *p++ = value;
}
static void Main() {
int[] a = new int[100];
488
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
unsafe {
fixed (int* p = a) Fill(p, 100, -1);
}
}
}
この例では,fixed文は配列を固定するので,そのアドレスはポインタを取るメソッドに渡
すことができる。
文字列インスタンスを固定して生成されるchar*値は,常にナル文字で終わる文字列を指す。文字列イ
ンスタンスsを指すポインタpを得たfixed文の中では,pからp + s.Length - 1までの範囲のポ
インタ値は,その文字列内の文字のアドレスを示し,ポインタ値p + s.Lengthは,常にナル文字(値
'¥0'をとる文字)を指す。
固定されたポインタを通じて管理型のオブジェクトを変更すると,未定義の振る舞いが発生する可能性
がある。
注記1 例えば,文字列は変更不可なので,固定された文字列へのポインタによって参照されている
文字が変更されないようにするのはプログラマの責任である。
注記2 文字列の自動的ナル文字終了は,特にC言語方式の文字列を期待するAPIを呼び出すときに
有用となる。ただし,文字列インスタンスはナル文字を内部に含むことができることに注意
する。そのようなナル文字が存在する場合には,ナル文字終了のchar*として扱われる文字
列は,そこで切り捨てられる。
27.7 スタック割当て
安全でない文脈では,局所変数宣言(15.5.1参照)は,呼出しスタックからメモリを割り当てるスタッ
ク割当て初期化子を含んでもよい。
≪局所変数初期化子≫:
≪式≫
≪配列初期化子≫
≪スタック割当て初期化子≫
≪スタック割当て初期化子≫:
stackalloc ≪非管理型≫ [ ≪式≫ ]
≪非管理型≫は,新たに割り当てられた場所に格納される項目の型を示し,≪式≫は,項目の個数を示
す。この二つによって,必要な割当ての大きさが指定される。スタック割当ての大きさは負の値にできな
いので,負の値に評価される定数式で項目数を指定すると,コンパイル時エラーになる。
形式stackalloc T[E]のスタック割当て初期化子では,Tは非管理型(27.2参照)でなければならず,
Eはint型に変換可能な型の式でなければならない。この構築要素は,呼出しスタックから
E * sizeof(T)バイトを割り当て,新しく割り当てた箇所へのT*型のポインタを返す。Eが負値の場合,
振る舞いは未定義とする。Eが0の場合,割当ては行われず,返されるポインタは実装定義とする。与え
られたサイズを割り当てるだけの十分なメモリがない場合,System.StackOverflowExceptionが送
出される。
新たに割り当てられるメモリの内容は未定義とする。
スタック割当て初期化子は,catchブロック及びfinallyブロックでは許されない(15.10参照)。
注記 stackallocを用いて割り当てたメモリを明示的に解放する方法はない。
489
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
関数メンバの実行中に生成されスタックに割り当てられたすべてのメモリブロックは,関数メンバが戻
るときに自動的に破棄される。
注記 これは,C及びC++実装での拡張であるalloca関数に対応している。
例
using System;
class Test
{
static string IntToString(int value) {
int n = value >= 0 ? value : -value;
unsafe {
char* buffer = stackalloc char[16];
char* p = buffer + 16;
do {
*--p = (char)(n % 10 + '0');
n /= 10;
} while (n != 0);
if (value < 0) *--p = '-';
return new string(p, 0, (int)(buffer + 16 - p));
}
}
static void Main() {
Console.WriteLine(IntToString(12345));
Console.WriteLine(IntToString(-999));
}
}
この例では,stackalloc初期化子は,メソッドIntToString内で使用されるスタック上に
16個の文字のバッファを割り当てる。このバッファはメソッドが戻るときに破棄される。
27.8 動的メモリ割当て
stackalloc演算子以外にはC#は,ごみ集めされないメモリに対する管理のためのあらかじめ定義さ
れた構成要素をもたない。そのような機能は,クラスライブラリを使って提供するか,基礎となっている
オペレーティングシステムから直接移入するのが典型的となる。
例 例えば,次の静的クラスMemoryは,C#からオペレーティングシステムのヒープ関数にどのよう
にしてアクセスするかを示す。
using System;
using System.Runtime.InteropServices;
public unsafe static class Memory
{
// Handle for the process heap. This handle is used in all calls to
// the HeapXXX APIs in the methods below.
static int ph = GetProcessHeap();
490
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
// Allocates a memory block of the given size. The allocated memory
is
// automatically initialized to zero.
public static void* Alloc(int size) {
void* result = HeapAlloc(ph, HEAP̲ZERO̲MEMORY, size);
if (result == null) throw new OutOfMemoryException();
return result;
}
// Copies count bytes from src to dst. The source and destination
// blocks are permitted to overlap.
public static void Copy(void* src, void* dst, int count) {
byte* ps = (byte*)src;
byte* pd = (byte*)dst;
if (ps > pd) {
for (; count != 0; count--) *pd++ = *ps++;
}
else if (ps < pd) {
for (ps += count, pd += count; count != 0; count--)
*--pd = *--ps;
}
}
// Frees a memory block.
public static void Free(void* block) {
if (!HeapFree(ph, 0, block)) throw new
InvalidOperationException();
}
// Re-allocates a memory block. If the reallocation request is for
a
// larger size, the additional region of memory is automatically
// initialized to zero.
public static void* ReAlloc(void* block, int size) {
void* result = HeapReAlloc(ph, HEAP̲ZERO̲MEMORY, block,
size);
if (result == null) throw new OutOfMemoryException();
return result;
}
// Returns the size of a memory block.
public static int SizeOf(void* block) {
int result = HeapSize(ph, 0, block);
if (result == -1) throw new InvalidOperationException();
491
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
return result;
}
// Heap API flags
const int HEAP̲ZERO̲MEMORY = 0x00000008;
// Heap API functions
[DllImport("kernel32")]
static extern int GetProcessHeap();
[DllImport("kernel32")]
static extern void* HeapAlloc(int hHeap, int flags, int size);
[DllImport("kernel32")]
static extern bool HeapFree(int hHeap, int flags, void* block);
[DllImport("kernel32")]
static extern void* HeapReAlloc(int hHeap, int flags,
void* block, int size);
[DllImport("kernel32")]
static extern int HeapSize(int hHeap, int flags, void* block);
}
次は,クラスMemoryを使用した例である。
class Test
{
static void Main() {
unsafe {
byte* buffer = (byte*)Memory.Alloc(256);
for (int i = 0; i < 256; i++) buffer[i] = (byte)i;
byte[] array = new byte[256];
fixed (byte* p = array) Memory.Copy(buffer, p, 256);
Memory.Free(buffer);
for (int i = 0; i < 256; i++)
Console.WriteLine(array[i]);
}
}
}
この例では,Memory.Allocを用いて256バイトのメモリを割り当て,0から255までの値で
メモリブロックを初期化する。次に,256の要素をもつバイト配列を割り当て,Memory.Copy
を用いてメモリブロックの内容をバイト配列にコピーする。最後にメモリブロックは
Memory.Freeを使って解放され,バイト配列の内容がコンソールに出力される。
492
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
附属書A
(参考)
文法
序文
この附属書は,本体の規定を補足するものであって,規定の一部ではない。
この附属書では,規格本体に見られる字句文法,構文文法及び安全でないコードのための文法拡張をま
とめて示す。文法生成規則は,規格本体に出現するのと同じ順序である。
A.1 字句文法
≪入力≫::
≪入力節≫opt
≪入力節≫::
≪入力節部≫
≪入力節≫ ≪入力節部≫
≪入力節部≫::
≪入力要素群≫opt ≪改行≫
≪前処理指令≫
≪入力要素群≫::
≪入力要素≫
≪入力要素群≫ ≪入力要素≫
≪入力要素≫::
≪空白類≫
≪注釈≫
≪字句≫
A.1.1 行終端子
≪改行≫::
復帰文字 (U+000D)
改行文字 (U+000A)
改行文字 (U+000A) に続く復帰文字 (U+000D)
次行文字 (U+0085)
行区切り文字 (U+2028)
段落区切り文字 (U+2029)
A.1.2 空白類
≪空白類≫::
≪空白類文字群≫
≪空白類文字群≫::
≪空白類文字≫
≪空白類文字群≫ ≪空白類文字≫
493
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪空白類文字≫::
Unicode クラス Zs の任意の文字
水平タブ文字 (U+0009)
垂直タブ文字 (U+000B)
改ページ文字 (U+000C)
A.1.3 注釈
≪注釈≫::
≪単一行注釈≫
≪区切り注釈≫
≪単一行注釈≫::
// ≪入力文字群≫opt
≪入力文字群≫::
≪入力文字≫
≪入力文字群≫ ≪入力文字≫
≪入力文字≫::
改行文字を除く任意のUnicode文字
≪改行文字≫::
復帰文字 (U+000D)
行送り文字 (U+000A)
次行文字 (U+0085)
行分離文字 (U+2028)
段落分離文字 (U+2029)
≪区切り注釈≫::
/* ≪区切り注釈テキスト≫opt ≪星印群≫/
≪区切り注釈テキスト≫::
≪区切り注釈節≫
≪区切り注釈テキスト≫ ≪区切り注釈節≫
≪区切り注釈節≫::
≪非星印≫
≪星印群≫≪非斜線≫
≪星印群≫::
*
≪星印群≫*
≪非星印≫::
* を除く任意のUnicode文字
≪非斜線≫::
/ を除く任意のUnicode文字
A.1.4 字句
≪字句≫::
≪識別子≫
494
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪キーワード≫
≪整数リテラル≫
≪実数リテラル≫
≪文字リテラル≫
≪文字列リテラル≫
≪演算子又は区切り子≫
A.1.5 Unicode逆斜線表記
≪Unicode逆斜線表記≫::
¥u≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字≫
¥U≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字≫≪16進数字
≫≪16進数字≫≪16進数字≫
A.1.6 識別子
≪識別子≫::
≪利用可能識別子≫
@≪識別子又はキーワード≫
≪利用可能識別子≫::
≪キーワード≫でない≪識別子又はキーワード≫
≪識別子又はキーワード≫::
≪識別子開始文字≫≪識別子部分文字群≫opt
≪識別子開始文字≫::
≪C#字≫
̲(下線 U+005F)
≪識別子部分文字群≫::
≪識別子部分文字≫
≪識別子部分文字群≫≪識別子部分文字≫
≪識別子部分文字≫::
≪C#字≫
≪10進数字文字≫
≪接続文字≫
≪結合文字≫
≪整形文字≫
≪C#字≫::
クラス Lu,Ll,Lt,Lm,Lo又は Nl の Unicode 文字
クラス Lu,Ll,Lt,Lm,Lo又は Nl の文字を表す ≪Unicode逆斜線表記≫
≪結合文字≫::
クラス Mn 又は Mc の Unicode 文字
クラス Mn 又は Mc の文字を表す ≪Unicode逆斜線表記≫
≪10進数字文字≫::
クラス Ndの Unicode 文字
クラス Nd の文字を表す ≪Unicode逆斜線表記≫
495
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪接続文字≫::
クラス Pc の Unicode 文字
クラス Pc の文字を表す ≪Unicode逆斜線表記≫
≪整形文字≫::
クラス Cfの Unicode 文字
クラス Cfの文字を表す ≪Unicode逆斜線表記≫
A.1.7 キーワード
keyword:: 次のいずれか
abstract
as
base
bool
break
byte
case
catch
char
checked
class
const
continue
decimal
default
delegate
do
double
else
enum
event
explicit
extern
false
finally
fixed
float
for
foreach
goto
if
implicit
in
int
interface
internal
is
lock
long
namespace
new
null
object
operator
out
override
params
private
protected
public
readonly
ref
return
sbyte
sealed
short
sizeof
stackalloc
static
string
struct
switch
this
throw
true
try
typeof
uint
ulong
unchecked
unsafe
ushort
using
virtual
void
volatile
while
A.1.8 リテラル
≪リテラル≫::
≪真理値リテラル≫
≪整数リテラル≫
≪実数リテラル≫
≪文字リテラル≫
≪文字列リテラル≫
≪nullリテラル≫
≪真理値リテラル≫::
true
false
≪整数リテラル≫::
≪10進整数リテラル≫
496
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪16進整数リテラル≫
≪10進リテラル≫::
≪10進数字群≫≪整数型接尾辞≫opt
≪10進数字群≫::
≪10進数字≫
≪10進数字群≫≪10進数字≫
≪10進数字≫:: 次のいずれか
0 1 2 3 4 5 6 7 8 9
≪整数型接尾辞≫:: 次のいずれか
U u L l UL Ul uL ul LU Lu lU lu
≪16進リテラル≫::
0x≪16進数字群≫≪整数型接尾辞≫opt
0X≪16進数字群≫≪整数型接尾辞≫opt
≪16進数字群≫::
≪16進数字≫
≪16進数字群≫≪16進数字≫
≪16進数字≫:: 次のいずれか
0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
≪実数リテラル≫::
≪10進数字群≫.≪10進数字群≫≪指数部≫opt≪実数接尾辞≫opt.≪10進数字群≫
≪指数部≫opt≪実数接尾辞≫opt
≪10進数字群≫≪指数部≫≪実数接尾辞≫opt
≪10進数字群≫≪実数接尾辞≫
≪指数部≫::
e≪符号≫opt≪10進数字群≫
E≪符号≫opt≪10進数字群≫
≪符号≫:: 次のいずれか
+ -
≪実数接尾辞≫:: 次のいずれか
F f D d M m
≪文字リテラル≫::
' ≪文字≫ '
≪文字≫::
≪単一文字≫
≪単純逆斜線表記≫
≪16進逆斜線表記≫
≪Unicode逆斜線表記≫
≪単一文字≫::
一重引用符 ʼ (U+0027),逆斜線 ¥ (U+005C) 及び 改行文字を除く任意の文字
≪単純逆斜線表記≫:: 次のいずれか
497
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
¥' ¥" ¥¥ ¥0 ¥a ¥b ¥f ¥n ¥r ¥t ¥v
≪16進逆斜線表記≫::
¥x≪16進数字≫≪16進数字≫opt≪16進数字≫opt≪16進数字≫opt
≪文字列リテラル≫::
≪通常文字列リテラル≫
≪逐語的文字列リテラル≫
≪通常文字列リテラル≫::
" ≪通常リテラル文字群≫opt "
≪通常リテラル文字列群≫::
≪通常文字列リテラル文字≫
≪通常文字列リテラル文字群≫ ≪通常文字列リテラル文字≫
≪通常文字列リテラル文字≫::
≪単一通常文字列リテラル文字≫
≪単純逆斜線表記≫
≪16進逆斜線表記≫
≪Unicode逆斜線表記≫
≪単一標準文字列リテラル文字≫::
二重引用符 ” (U+0022),逆斜線 ¥ (U+005C) 及び 改行文字以外の任意の文字
≪逐語的文字列リテラル≫::
@" ≪逐語的文字列リテラル文字群≫opt "
≪逐語的文字列リテラル文字群≫::
≪逐語的文字列リテラル文字≫
≪逐語的文字列リテラル文字群≫ ≪逐語的文字列リテラル文字≫
≪逐語的文字列リテラル文字≫::
≪単一逐語的文字列リテラル文字≫
≪引用符二重表記≫
≪単一逐語的文字列リテラル文字≫::
二重引用符 ” 以外の任意の文字
≪引用符二重表記≫::
""
≪nullリテラル≫::
null
A.1.9 演算子及び区切り子
≪演算子又は区切り子≫:: 次のいずれか
{
}
[
]
(
)
.
,
:
;
+
-
*
/
%
&
|
^
!
~
=
<
>
?
??
::
++
--
&&
||
498
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
->
==
!=
<=
>=
+=
-=
*=
/=
%=
&=
|=
^=
<<
<<=
≪右シフト≫::
> >
≪右シフト代入≫::
> >=
A.1.10 前処理指令
≪前処理指令≫::
≪前処理宣言≫
≪前処理条件≫
≪前処理行≫
≪前処理診断≫
≪前処理領域≫
≪前処理プラグマ≫
≪条件用記号≫::
識別子
true又はfalse以外の任意の≪キーワード≫
≪前処理式≫::
≪空白類≫opt ≪前処理or式≫ ≪空白類≫opt
≪前処理or式≫::
≪前処理and式≫
≪前処理or式≫ ≪空白類≫opt || ≪空白類≫opt ≪前処理and式≫
≪前処理and式≫::
≪前処理等価式≫
≪前処理and式≫ ≪空白類≫opt && ≪空白類≫opt ≪前処理等価式≫
≪前処理等価式≫::
≪前処理単項式≫
≪前処理等価式≫ ≪空白類≫opt == ≪空白類≫opt ≪前処理単項式≫
≪前処理等価式≫ ≪空白類≫opt != ≪空白類≫opt ≪前処理単項式≫
≪前処理単項式≫::
≪前処理一次式≫
! ≪空白類≫opt ≪前処理単項式≫
≪前処理一次式≫::
true
false
≪条件用記号≫
( ≪空白類≫opt ≪前処理式≫ ≪空白類≫opt )
≪前処理宣言≫::
499
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪空白類≫opt # ≪空白類≫opt define ≪空白類≫ ≪条件用記号≫ ≪前処
理改行≫
≪空白類≫opt # ≪空白類≫opt undef ≪空白類≫ ≪条件用記号≫ ≪前処理
改行≫
≪前処理改行≫::
≪空白類≫opt ≪単一行注釈≫opt ≪改行≫
≪前処理条件≫::
≪前処理if節≫ ≪前処理elif節群≫opt ≪前処理else節≫opt ≪前処理endif
≫
≪前処理if節≫::
≪空白類≫opt # ≪空白類≫opt if ≪空白類≫ ≪前処理式≫ ≪前処理改行≫
≪条件節≫opt
≪前処理elif節群≫::
≪前処理elif節≫
≪前処理elif節群≫ ≪前処理elif節≫
≪前処理elif節≫::
≪空白類≫opt # ≪空白類≫opt elif ≪空白類≫ ≪前処理式≫ ≪前処理改行
≫ ≪条件節≫opt
≪前処理else節≫::
≪空白類≫opt # ≪空白類≫opt else ≪前処理改行≫ ≪条件節≫opt
≪前処理endif≫::
≪空白類≫opt # ≪空白類≫opt endif ≪前処理改行≫
≪条件節≫::
≪入力節≫
≪読み飛ばし節≫
≪読み飛ばし節≫::
≪読み飛ばし節部≫
≪読み飛ばし節≫ ≪読み飛ばし節部≫
≪読み飛ばし節部≫::
≪空白類≫opt ≪読み飛ばし文字群≫opt ≪改行≫
≪前処理宣言≫
≪読み飛ばし文字群≫::
≪非#≫ ≪入力文字群≫opt
≪非#≫::
# を除く任意の≪入力文字≫
≪前処理行≫::
≪空白類≫opt # ≪空白類≫opt line ≪空白類≫opt ≪行表示子≫ ≪前処理
改行≫
≪行表示子≫::
≪10進数字群≫ ≪空白類≫ ≪ファイル名≫
500
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪10進数字群≫
≪識別子又はキーワード≫
≪ファイル名≫::
" ≪ファイル名文字群≫ "
≪ファイル名文字群≫::
≪ファイル名文字≫
≪ファイル名文字群≫ ≪ファイル名文字≫
≪ファイル名文字≫::
二重引用符 ” (U+0022)及び 改行文字を除く任意の文字
≪前処理診断≫::
≪空白類≫opt # ≪空白類≫opt error ≪空白類≫opt ≪前処理メッセージ≫
≪空白類≫opt # ≪空白類≫opt warning ≪空白類≫opt ≪前処理メッセージ≫
≪前処理メッセージ≫::
≪改行≫
≪空白類≫ ≪入力文字群≫opt ≪改行≫
≪前処理領域≫::
≪前処理領域開始≫ ≪条件節≫opt ≪前処理領域終了≫
≪前処理領域開始≫::
≪空白類≫opt # ≪空白類≫opt region ≪空白類≫opt ≪前処理メッセージ≫
≪前処理領域終了≫::
≪空白類≫opt # ≪空白類≫opt endregion ≪空白類≫opt ≪前処理メッセー
ジ≫
≪前処理プラグマ≫::
≪空白類≫opt # ≪空白類≫opt pragma ≪空白類≫opt ≪前処理プラグマテキ
スト≫
≪前処理プラグマテキスト≫::
≪改行≫
≪空白類≫ ≪入力文字群≫opt ≪改行≫
A.2 構文文法
A.2.1 基本概念
≪コンパイル単位≫:
≪外部別名指令群≫opt ≪using指令群≫opt ≪大域的属性≫opt ≪名前空間メン
バ宣言群≫opt
≪名前空間名≫:
≪名前空間名又は型名≫
≪型名≫:
≪名前空間名又は型名≫
≪名前空間名又は型名≫:
≪識別子≫ ≪型実引数並び≫opt
501
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪限定別名メンバ≫
≪名前空間名又は型名≫ . ≪識別子≫ ≪型実引数並び≫opt
A.2.2 型
≪型≫:
≪値型≫
≪参照型≫
≪型仮引数≫
≪値型≫:
≪構造体型≫
≪列挙型≫
≪構造体型≫:
≪型名≫
≪単純型≫
≪null許容型≫
≪単純型≫:
≪数値型≫
bool
≪数値型≫:
≪整数型≫
≪浮動小数点型≫
decimal
≪整数型≫:
sbyte
byte
short
ushort
int
uint
long
ulong
char
≪浮動小数点型≫:
float
double
≪列挙型≫:
≪型名≫
≪null許容型≫:
≪null許容でない値型≫ ?
≪null許容でない値型≫:
≪列挙型≫
502
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪型名≫
≪単純型≫
≪参照型≫:
≪クラス型≫
≪インタフェース型≫
≪配列型≫
≪委譲型≫
≪クラス型≫:
≪型名≫
object
string
≪インタフェース型≫:
≪型名≫
≪配列型≫:
≪非配列型≫ ≪位階指定子群≫
≪非配列型≫:
≪値型≫
≪クラス型≫
≪インタフェース型≫
≪配列型≫
≪委譲型≫
≪型仮引数≫
≪位階指定子群≫:
≪位階指定子≫
≪位階指定子群≫ ≪位階指定子≫
≪位階指定子≫:
[ ≪次元区切り子群≫opt ]
≪次元区切り子群≫:
,
≪次元区切り子群≫ ,
≪委譲型≫:
≪型名≫
A.2.3 変数
≪変数参照≫:
≪式≫
A.2.4 式
≪実引数並び≫:
≪実引数≫
≪実引数並び≫ , ≪実引数≫
≪実引数≫:
503
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪式≫
ref ≪変数参照≫
out ≪変数参照≫
≪一次式≫:
≪配列生成式≫
≪一次非配列生成式≫
≪一次非配列生成式≫:
≪リテラル≫
≪単純名≫
≪括弧付き式≫
≪メンバアクセス≫
≪呼出し式≫
≪要素アクセス≫
≪thisアクセス≫
≪baseアクセス≫
≪後置増加式≫
≪後置減少式≫
≪オブジェクト生成式≫
≪委譲生成式≫
≪typeof式≫
≪checked式≫
≪unchecked式≫
≪省略時値式≫
≪無名メソッド式≫
≪単純名≫:
≪識別子≫
≪型実引数並び≫opt
≪括弧付き式≫:
( ≪式≫ )
≪メンバアクセス≫:
≪一次式≫ . ≪識別子≫ ≪型実引数並び≫opt
≪あらかじめ定義された型≫ . ≪識別子≫ ≪型実引数並び≫opt
≪限定別名メンバ≫ . ≪識別子≫ ≪型実引数並び≫opt
≪あらかじめ定義された型≫: 次のいずれか
bool
byte
char
decimal
double
float int
long
object sbyte short string
uint
ulong ushort
≪呼出し式≫:
≪一次式≫ ( ≪実引数並び≫opt )
≪要素アクセス≫:
≪一次非配列生成式≫ [ ≪式並び≫ ]
504
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪式並び≫:
≪式≫
≪式並び≫ , ≪式≫
≪thisアクセス≫:
this
≪baseアクセス≫:
base . ≪識別子≫
≪型実引数並び≫opt
base [ ≪式並び≫ ]
≪後置増加式≫:
≪一次式≫ ++
≪後置減少式≫:
≪一次式≫ --
≪オブジェクト生成式≫:
new ≪型≫ ( ≪実引数並び≫opt )
≪配列生成式≫:
new ≪非配列型≫ [ ≪式並び≫ ] ≪位階指定子群≫opt ≪配列初期化子≫opt
new ≪配列型≫ ≪配列初期化子≫
≪委譲生成式≫:
new ≪委譲型≫ ( ≪式≫ )
≪typeof式≫:
typeof ( ≪型≫ )
typeof ( ≪非束縛型名≫ )
typeof ( void )
≪非束縛型名≫:
≪識別子≫ ≪総称次元指定子≫opt
≪識別子≫ :: ≪識別子≫ ≪総称次元指定子≫opt
≪非束縛型名≫ . ≪識別子≫ ≪総称次元指定子≫opt
≪総称次元指定子≫:
< ≪コンマ群≫ >
≪コンマ群≫:
,
≪コンマ群≫ ,
≪checked式≫:
checked ( ≪式≫ )
≪unchecked式≫:
unchecked ( ≪式≫ )
≪省略時値式≫:
default ( ≪型≫ )
≪無名メソッド式≫:
delegate ≪無名メソッド呼出し情報≫opt ≪ブロック≫
505
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪無名メソッド呼出し情報≫:
( ≪無名メソッド仮引数並び≫opt )
≪無名メソッド仮引数並び≫:
≪無名メソッド仮引数≫
≪無名メソッド仮引数並び≫ , ≪無名メソッド仮引数≫
≪無名メソッド仮引数≫:
≪仮引数修飾子≫opt ≪式≫ ≪識別子≫
≪単項式≫:
≪一次式≫
+ ≪単項式≫
- ≪単項式≫
! ≪単項式≫
~ ≪単項式≫
≪前置増加式≫
≪前置減少式≫
≪キャスト式≫
≪前置増加式≫:
++ ≪単項式≫
≪前置減少式≫:
-- ≪単項式≫
≪キャスト式≫:
( ≪型≫ ) ≪単項式≫
≪乗除式≫:
≪単項式≫
≪乗除式≫ * ≪単項式≫
≪乗除式≫ / ≪単項式≫
≪乗除式≫ % ≪単項式≫
≪加減式≫:
≪乗除式≫
≪加減式≫ + ≪乗除式≫
≪加減式≫ ‒ ≪乗除式≫
≪シフト式≫:
≪加減式≫
≪シフト式≫ << ≪加減式≫
≪シフト式≫ ≪右シフト≫ ≪加減式≫
≪関係式≫:
≪シフト式≫
≪関係式≫ < ≪シフト式≫
≪関係式≫ > ≪シフト式≫
≪関係式≫ <= ≪シフト式≫
506
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪関係式≫ >= ≪シフト式≫
≪関係式≫ is ≪型≫
≪関係式≫ as ≪型≫
≪等価式≫:
≪関係式≫
≪等価式≫ == ≪関係式≫
≪等価式≫ != ≪関係式≫
≪論理積式≫:
≪等価式≫
≪論理積式≫ & ≪等価式≫
≪排他的論理和式≫:
≪論理積式≫
≪排他的論理和式≫ ^ ≪論理積式≫
≪論理和式≫:
≪排他的論理和式≫
≪論理和式≫ | ≪排他的論理和式≫
≪二択条件論理積式≫:
≪論理和式≫
≪二択条件論理積式≫ && ≪論理和式≫
≪二択条件論理和式≫:
≪二択条件論理積式≫
≪二択条件論理和式≫ || ≪二択条件論理積式≫
≪null判定選択式≫:
≪二択条件論理和式≫
≪二択条件論理和式≫ ?? ≪null判定選択式≫
≪二択条件式≫:
≪null判定選択式≫
≪null判定選択式≫ ? ≪式≫ : ≪式≫
≪代入≫:
≪単項式≫ ≪代入演算子≫ ≪式≫
≪代入演算子≫: 次のいずれか
= += -= *= /= %= &= |= ^= <<= ≪右シフト代入≫
≪式≫:
≪二択条件式≫
≪代入≫
≪定数式≫:
≪式≫
≪真理式≫:
≪式≫
507
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
A.2.5 文
≪文≫:
≪ラベル付き文≫
≪宣言文≫
≪埋込み文≫
≪埋込み文≫:
≪ブロック≫
≪空文≫
≪式文≫
≪選択文≫
≪繰返し文≫
≪飛越し文≫
≪try文≫
≪checked文≫
≪unchecked文≫
≪lock文≫
≪using文≫
≪yield文≫
≪ブロック≫:
{ ≪文並び≫opt }
≪文並び≫:
≪文≫
≪文並び≫ ≪文≫
≪空文≫:
;
≪ラベル付き文≫:
≪識別子≫ : ≪文≫
≪宣言文≫:
≪局所変数宣言≫ ;
≪局所定数宣言≫ ;
≪局所変数宣言≫:
≪型≫ ≪局所変数宣言子群≫
≪局所変数宣言子群≫:
≪局所変数宣言子≫
≪局所変数宣言子群≫ , ≪局所変数宣言子≫
≪局所変数宣言子≫:
≪識別子≫
≪識別子≫ = ≪局所変数初期化子≫
≪局所変数初期化子≫:
≪式≫
508
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪配列初期化子≫
≪局所定数宣言≫:
const ≪型≫ ≪定数宣言子群≫
≪定数宣言子群≫:
≪定数宣言子≫
≪定数宣言子群≫ , ≪定数宣言子≫
≪定数宣言子≫:
≪識別子≫ = ≪定数式≫
≪式文≫:
≪文式≫ ;
≪文式≫:
≪呼出し式≫
≪オブジェクト生成式≫
≪代入≫
≪後置増加式≫
≪後置減少式≫
≪前置増加式≫
≪前置減少式≫
≪選択文≫:
≪if文≫
≪switch文≫
≪if文≫:
if ( ≪真理式≫ ) ≪埋込み文≫
if ( ≪真理式≫ ) ≪埋込み文≫ else ≪埋込み文≫
≪switch文≫:
switch ( ≪式≫ ) ≪switchブロック≫
≪switchブロック≫:
{ ≪switch節群≫opt }
≪switch節群≫:
≪switch節≫
≪switch節群≫ ≪switch節≫
≪switch節≫:
≪switchラベル群≫ ≪式並び≫
≪switchラベル群≫:
≪switchラベル≫
≪switchラベル群≫ ≪switchラベル≫
≪switchラベル≫:
case ≪定数式≫ :
default :
≪繰返し文≫:
509
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪while文≫
≪do文≫
≪for文≫
≪foreach文≫
≪while文≫:
while ( ≪真理式≫ ) ≪埋込み文≫
≪do文≫:
do ≪埋込み文≫ while ( ≪真理式≫ ) ;
≪for文≫:
for ( ≪for初期化子≫opt ; ≪for条件≫opt ; ≪for反復子≫opt ) ≪埋
込み文≫
≪for初期化子≫:
≪局所変数宣言≫
≪文式並び≫
≪for条件≫:
≪真理式≫
≪for反復子≫:
≪文式並び≫
≪文式並び≫:
≪文式≫
≪文式並び≫ , ≪文式≫
≪foreach文≫:
foreach ( ≪型≫ ≪識別子≫ in ≪式≫ ) ≪埋込み文≫
≪飛越し文≫:
≪break文≫
≪continue文≫
≪goto文≫
≪return文≫
≪throw文≫
≪break文≫:
break ;
≪continue文≫:
continue ;
≪goto文≫:
goto ≪識別子≫ ;
goto case ≪定数式≫ ;
goto default ;
≪return文≫:
return ≪式≫opt ;
≪throw文≫:
510
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
throw ≪式≫opt ;
≪try文≫:
try ≪ブロック≫ ≪catch節群≫
try ≪ブロック≫ ≪catch節群≫opt ≪finally節≫
≪catch節群≫:
≪特定catch節群≫
≪特定catch節群≫opt ≪全般catch節≫
≪特定catch節群≫:
≪特定catch節≫
≪特定catch節群≫ ≪特定catch節≫
≪特定catch節≫:
catch ( ≪クラス型≫ ≪識別子≫opt ) ≪ブロック≫
≪全般catch節≫:
catch ≪ブロック≫
≪finally節≫:
finally ≪ブロック≫
≪checked文≫:
checked ≪ブロック≫
≪unchecked文≫:
unchecked ≪ブロック≫
≪lock文≫:
lock ( ≪式≫ ) ≪埋込み文≫
≪using文≫:
using ( ≪資源取得子≫ ) ≪埋込み文≫
≪資源取得子≫:
≪局所変数宣言≫
≪式≫
≪yield文≫:
yield return ≪式≫ ;
yield break ;
≪名前空間宣言≫:
namespace ≪限定付き識別子≫ ≪名前空間本体≫ ;opt
≪限定付き識別子≫:
≪識別子≫
≪限定付き識別子≫ . ≪識別子≫
≪名前空間本体≫:
{ ≪外部別名指令群≫opt ≪using指令群≫opt ≪名前空間メンバ宣言群≫
opt }
≪外部別名指令≫:
≪外部別名指令≫
511
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪外部別名指令群≫ ≪外部別名指令≫
≪外部別名指令群≫:
extern alias ≪識別子≫ ;
≪using指令群≫:
≪using指令≫
≪using指令群≫ ≪using指令≫
≪using指令≫:
≪using別名指令≫
≪using名前空間指令≫
≪using別名指令≫:
using ≪識別子≫ = ≪名前空間又は型名≫ ;
≪using名前空間指令≫:
using ≪名前空間名≫ ;
≪名前空間メンバ宣言群≫:
≪名前空間メンバ宣言≫
≪名前空間メンバ宣言群≫ ≪名前空間メンバ宣言≫
≪名前空間メンバ宣言≫:
≪名前空間宣言≫
≪型宣言≫
≪型宣言≫:
≪クラス宣言≫
≪構造体宣言≫
≪インタフェース宣言≫
≪列挙宣言≫
≪委譲宣言≫
≪限定別名メンバ≫:
≪識別子≫ :: ≪識別子≫ ≪型実引数並び≫opt
A.2.6 クラス
≪クラス宣言≫:
≪属性群≫opt ≪クラス修飾子群≫opt partical opt class ≪識別子≫ ≪型実
引数並び≫opt ≪クラス基底≫opt ≪型仮引数制約群節群≫opt ≪クラス本体≫ ;opt
≪クラス修飾子群≫:
≪クラス修飾子≫
≪クラス修飾子群≫ ≪クラス修飾子≫
≪クラス修飾子≫:
new
public
protected
internal
512
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
private
abstract
sealed
static
≪クラス基底≫:
: ≪クラス型≫
: ≪インタフェース型並び≫
: ≪クラス型≫ , ≪インタフェース型並び≫
≪インタフェース型並び≫:
≪インタフェース型≫
≪インタフェース型並び≫ , ≪インタフェース型≫
≪クラス本体≫:
{ ≪クラスメンバ宣言群≫opt }
≪クラスメンバ宣言群≫:
≪クラスメンバ宣言≫
≪クラスメンバ宣言群≫ ≪クラスメンバ宣言≫
≪クラスメンバ宣言≫:
≪定数宣言≫
≪フィールド宣言≫
≪メソッド宣言≫
≪特性宣言≫
≪イベント宣言≫
≪添字子宣言≫
≪演算子宣言≫
≪構築子宣言≫
≪終了化子宣言≫
≪静的構築子宣言≫
≪型宣言≫
≪定数宣言≫:
≪属性群≫opt ≪定数修飾子群≫opt const ≪型≫ ≪定数宣言子群≫ ;
≪定数修飾子群≫:
≪定数修飾子≫
≪定数修飾子群≫ ≪定数修飾子≫
≪定数修飾子≫:
new
public
protected
internal
private
≪定数宣言子群≫:
513
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪定数宣言子≫
≪定数宣言子群≫ , ≪定数宣言子≫
≪定数宣言子≫:
≪識別子≫ = ≪定数式≫
≪フィールド宣言≫:
≪属性群≫opt ≪フィールド修飾子群≫opt ≪型≫ ≪変数宣言子群≫ ;
≪フィールド修飾子群≫:
≪フィールド修飾子≫
≪フィールド修飾子群≫ ≪フィールド修飾子≫
≪フィールド修飾子≫:
new
public
protected
internal
private
static
readonly
volatile
≪変数宣言子群≫:
≪変数宣言子≫
≪変数宣言子群≫ , ≪変数宣言子≫
≪変数宣言子≫:
≪識別子≫
≪識別子≫ = ≪変数初期化子≫
≪変数初期化子≫:
≪式≫
≪配列初期化子≫
≪メソッド宣言≫:
≪メソッドヘッダ≫ ≪メソッド本体≫
≪メソッドヘッダ≫:
≪属性群≫opt ≪メソッド修飾子群≫opt ≪返却値型≫ ≪メンバ名≫ ≪型仮引数
並び≫opt ( ≪仮引数並び≫opt ) ≪型仮引数制約群節群≫opt
≪メソッド修飾子群≫:
≪メソッド修飾子≫
≪メソッド修飾子群≫ ≪メソッド修飾子≫
≪メソッド修飾子≫:
new
public
protected
internal
514
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
private
static
virtual
sealed
override
abstract
extern
≪返却値型≫:
≪型≫
void
≪メンバ名≫:
≪識別子≫
≪インタフェース型≫ . ≪識別子≫
≪メソッド本体≫:
≪ブロック≫
;
≪仮引数並び≫:
≪固定仮引数群≫
≪固定仮引数群≫ , ≪仮引数配列≫
≪仮引数配列≫
≪固定仮引数群≫:
≪固定仮引数≫
≪固定仮引数群≫ , ≪固定仮引数≫
≪固定仮引数≫:
≪属性群≫opt ≪仮引数修飾子≫opt ≪型≫ ≪識別子≫
≪仮引数修飾子≫:
ref
out
≪仮引数配列≫:
≪属性群≫opt params ≪配列型≫ ≪識別子≫
≪特性宣言≫:
≪属性群≫opt ≪特性修飾子群≫opt ≪型≫ ≪メンバ名≫ { ≪アクセス子
宣言群≫ }
≪特性修飾子群≫:
≪特性修飾子≫
≪特性修飾子群≫ ≪特性修飾子≫
≪特性修飾子≫:
new
public
protected
515
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
internal
private
static
virtual
sealed
override
abstract
extern
≪アクセス子宣言群≫:
≪getアクセス子宣言≫ ≪setアクセス子宣言≫opt
≪setアクセス子宣言≫ ≪getアクセス子宣言≫opt
≪getアクセス子宣言≫:
≪属性群≫opt ≪アクセス子修飾子≫opt get ≪アクセス子本体≫
≪setアクセス子宣言≫:
≪属性群≫opt ≪アクセス子修飾子≫opt set ≪アクセス子本体≫
≪アクセス子修飾子≫:
protected
internal
private
protected internal
internal protected
≪アクセス子本体≫:
≪ブロック≫
;
≪イベント宣言≫:
≪属性群≫opt ≪イベント修飾子群≫opt event 型 ≪変数宣言子群≫ ;
≪属性群≫opt ≪イベント修飾子群≫opt event 型 ≪メンバ名≫ { ≪
イベントアクセス子宣言群≫ }
≪イベント修飾子群≫:
≪イベント修飾子≫
≪イベント修飾子群≫ ≪イベント修飾子≫
≪イベント修飾子≫:
new
public
protected
internal
private
static
virtual
sealed
516
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
override
abstract
extern
≪イベントアクセス子宣言群≫:
≪addアクセス子宣言≫ ≪removeアクセス子宣言≫
≪removeアクセス子宣言≫ ≪addアクセス子宣言≫
≪addアクセス子宣言≫:
≪属性群≫opt add ≪ブロック≫
≪removeアクセス子宣言≫:
≪属性群≫opt remove ≪ブロック≫
≪添字子宣言≫:
≪属性群≫opt ≪添字子修飾子群≫opt ≪添字子宣言子≫ { ≪アクセス子宣
言群≫ }
≪添字子修飾子群≫:
≪添字子修飾子≫
≪添字子修飾子群≫ ≪添字子修飾子≫
≪添字子修飾子≫:
new
public
protected
internal
private
virtual
sealed
override
abstract
extern
≪添字子宣言子≫:
≪型≫ this [ ≪仮引数並び≫ ]
≪型≫ ≪インタフェース型≫ . this [ ≪仮引数並び≫ ]
≪演算子宣言≫:
≪属性群≫opt ≪演算子修飾子群≫ ≪演算子宣言子≫ ≪演算子本体≫
≪演算子修飾子群≫:
≪演算子修飾子≫
≪演算子修飾子群≫ ≪演算子修飾子≫
≪演算子修飾子≫:
public
static
extern
≪演算子宣言子≫:
517
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪単項演算子宣言子≫
≪2項演算子宣言子≫
≪変換演算子宣言子≫
≪単項演算子宣言子≫:
≪型≫ operator ≪多重定義可能単項演算子≫ ( ≪型≫ ≪識別子
≫ )
≪多重定義可能単項演算子≫: 次のいずれか
+ - ! ~ ++ -- true false
≪2項演算子宣言子≫:
≪型≫ operator ≪多重定義可能2項演算子≫ ( ≪型≫ ≪識別子≫ ,
≪型≫ ≪識別子≫ )
≪多重定義可能2項演算子≫: 次のいずれか
+ - * / % & | ^ << ≪右シフト≫ == != > < >=
<=
≪変換演算子宣言子≫:
implicit operator ≪型≫ ( ≪型≫ ≪識別子≫ )
explicit operator ≪型≫ ( ≪型≫ ≪識別子≫ )
≪演算子本体≫:
≪ブロック≫
;
≪構築子宣言≫:
≪属性群≫opt ≪構築子修飾子群≫opt ≪構築子宣言子≫ ≪構築子本体≫
≪構築子修飾子群≫:
≪構築子修飾子≫
≪構築子修飾子群≫ ≪構築子修飾子≫
≪構築子修飾子≫:
public
protected
internal
private
extern
≪構築子宣言子≫:
≪識別子≫ ( ≪仮引数並び≫opt ) ≪構築子初期化子≫opt
≪構築子初期化子≫:
: base ( ≪実引数並び≫opt )
: this ( ≪実引数並び≫opt )
≪構築子本体≫:
≪ブロック≫
;
≪静的構築子宣言≫:
518
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪属性群≫opt ≪静的構築子修飾子群≫ ≪識別子≫ ( ) ≪静的構築子本体
≫
≪静的構築子修飾子群≫:
externopt static
static externopt
≪静的構築子本体≫:
≪ブロック≫
;
≪終了化子宣言≫:
≪属性群≫opt externopt ~ ≪識別子≫ ( ) ≪終了化子本体≫
≪終了化子本体≫:
≪ブロック≫
;
A.2.7 構造体
構造体宣言:
≪属性群≫opt ≪構造体修飾子群≫opt partial opt struct ≪識別子≫
≪型仮引数並び≫opt ≪構造体インタフェース群≫opt ≪型仮引数制約群節群
≫opt
≪構造体本体≫ ;opt
≪構造体修飾子群≫:
≪構造体修飾子≫
≪構造体修飾子群≫ ≪構造体修飾子≫
≪構造体修飾子≫:
new
public
protected
internal
private
≪構造体インタフェース群≫:
: ≪インタフェース型並び≫
≪構造体本体≫:
{ ≪構造体メンバ宣言群≫opt }
≪構造体メンバ宣言群≫:
≪構造体メンバ宣言≫
≪構造体メンバ宣言群≫ ≪構造体メンバ宣言≫
≪構造体メンバ宣言≫:
≪定数宣言≫
≪フィールド宣言≫
≪メソッド宣言≫
≪特性宣言≫
519
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪イベント宣言≫
≪添字子宣言≫
≪演算子宣言≫
≪構築子宣言≫
≪静的構築子宣言≫
≪型宣言≫
A.2.8 配列
≪配列型≫:
≪非配列型≫ ≪位階指定子群≫
≪非配列型≫:
≪値型≫
≪クラス型≫
≪インタフェース型≫
≪委譲型≫
≪型仮引数≫
≪位階指定子群≫:
≪位階指定子≫
≪位階指定子群≫ ≪位階指定子≫
≪位階指定子≫:
[ ≪次元区切り子群≫opt ]
≪次元区切り子群≫:
,
≪次元区切り子群≫ ,
≪配列初期化子≫:
{ ≪変数初期化子並び≫opt }
{ ≪変数初期化子並び≫ , }
≪変数初期化子並び≫:
≪変数初期化子≫
≪変数初期化子並び≫ , ≪変数初期化子≫
≪変数初期化子≫:
≪式≫
≪配列初期化子≫
A.2.9 インタフェース
≪インタフェース宣言≫:
≪属性群≫opt ≪インタフェース修飾子群≫opt partial opt interface
≪識別子≫ ≪型仮引数並び≫opt ≪インタフェース基底≫opt
≪型仮引数制約群節群≫opt ≪インタフェース本体≫ ;opt
≪インタフェース修飾子群≫:
≪インタフェース修飾子≫
≪インタフェース修飾子群≫ ≪インタフェース修飾子≫
520
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪インタフェース修飾子≫:
new
public
protected
internal
private
≪インタフェース基底≫:
: ≪インタフェース型並び≫
≪インタフェース本体≫:
{ ≪インタフェースメンバ宣言群≫opt }
≪インタフェースメンバ宣言群≫:
≪インタフェースメンバ宣言≫
≪インタフェースメンバ宣言群≫ ≪インタフェースメンバ宣言≫
≪インタフェースメンバ宣言≫:
≪インタフェースメソッド宣言≫
≪インタフェース特性宣言≫
≪インタフェースイベント宣言≫
≪インタフェース添字子宣言≫
≪インタフェースメソッド宣言≫:
≪属性群≫opt newopt ≪返却値型≫ ≪識別子≫ ≪型仮引数並び≫opt
( ≪仮引数並び≫opt ) ≪型仮引数制約群節群≫opt ;
≪インタフェース特性宣言≫:
≪属性群≫opt newopt ≪型≫ ≪識別子≫ { ≪インタフェースアクセス子
群≫ }
≪インタフェースアクセス子群≫:
≪属性群≫opt get ;
≪属性群≫opt set ;
≪属性群≫opt get ; ≪属性群≫opt set ;
≪属性群≫opt set ; ≪属性群≫opt get ;
≪インタフェースイベント宣言≫:
≪属性群≫opt newopt event ≪型≫ ≪識別子≫ ;
≪インタフェース添字子宣言≫:
≪属性群≫opt newopt ≪型≫ this [ ≪仮引数並び≫ ] { ≪イン
タフェースアクセス子群≫ }
A.2.10 enum
≪列挙宣言≫:
≪属性群≫opt ≪列挙修飾子群≫opt enum ≪識別子≫ ≪列挙基底≫opt ≪
列挙本体≫ ;opt
≪基礎とする型≫:
: ≪整数型≫
521
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪列挙本体≫:
{ ≪列挙メンバ宣言群≫opt }
{ ≪列挙メンバ宣言群≫ , }
≪列挙修飾子群≫:
≪列挙修飾子≫
≪列挙修飾子群≫ ≪列挙修飾子≫
≪列挙修飾子≫:
new
public
protected
internal
private
≪列挙メンバ宣言群≫:
≪列挙メンバ宣言≫
≪列挙メンバ宣言群≫ , ≪列挙メンバ宣言≫
≪列挙メンバ宣言≫:
≪属性群≫opt ≪識別子≫
≪属性群≫opt ≪識別子≫ = ≪定数式≫
A.2.11 委譲
≪委譲宣言≫:
≪属性群≫opt ≪委譲修飾子群≫opt delegate ≪型≫ ≪識別子≫
≪型仮引数並び≫opt ( ≪仮引数並び≫opt ) ≪型仮引数制約群節群≫opt ;
≪委譲修飾子群≫:
≪委譲修飾子≫
≪委譲修飾子群≫ ≪委譲修飾子≫
≪委譲修飾子≫:
new
public
protected
internal
private
A.2.12 属性
≪大域的属性群≫:
≪大域的属性節群≫
≪大域的属性節群≫:
≪大域的属性節≫
≪大域的属性節群≫ ≪大域的属性節≫
≪大域的属性節≫:
[ ≪大域的属性指定定義≫ ≪属性並び≫ ]
[ ≪大域的属性指定定義≫ ≪属性並び≫ , ]
522
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪大域的属性指定定義≫:
≪大域的属性指定≫ :
≪大域的属性指定≫:
≪識別子≫
≪キーワード≫
≪属性群≫:
≪属性節群≫
≪属性節群≫:
≪属性節≫
≪属性節群≫ ≪属性節≫
≪属性節≫:
[ ≪属性指定定義≫opt ≪属性並び≫ ]
[ ≪属性指定定義≫opt ≪属性並び≫ , ]
≪属性指定定義≫:
≪属性指定≫ :
≪属性指定≫:
≪識別子≫
≪キーワード≫
≪属性並び≫:
≪属性≫
≪属性並び≫ , ≪属性≫
≪属性≫:
≪属性名≫ ≪属性実引数群≫opt
≪属性名≫:
≪型名≫
≪属性実引数群≫:
( ≪順序指定実引数並び≫opt )
( ≪順序指定実引数並び≫ , ≪名前付き実引数並び≫ )
( ≪名前付き実引数並び≫ )
≪順序指定実引数並び≫:
≪順序指定実引数≫
≪順序指定実引数並び≫ , ≪順序指定実引数≫
≪順序指定実引数≫:
≪属性実引数式≫
≪名前付き実引数並び≫:
≪名前付き実引数≫
≪名前付き実引数並び≫ , ≪名前付き実引数≫
≪名前付き実引数≫:
≪識別子≫ = ≪属性実引数式≫
≪属性実引数式≫:
523
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
≪式≫
A.2.13 総称性
≪型仮引数並び≫:
< ≪型仮引数群≫ >
≪型仮引数群≫:
≪属性群≫opt ≪型仮引数≫
≪型仮引数群≫ , ≪属性群≫opt ≪型仮引数≫
≪型仮引数≫:
≪識別子≫
≪型実引数並び≫:
< ≪型実引数群≫ >
≪型実引数群≫:
≪型実引数≫
≪型実引数群≫ , ≪型実引数≫
≪型実引数≫:
≪型≫
≪型仮引数制約群節群≫:
≪型仮引数制約群節≫
≪型仮引数制約群節群≫ ≪型仮引数制約群節≫
≪型仮引数制約群節≫:
where ≪型仮引数≫ : ≪型仮引数制約群≫
≪型仮引数制約群≫:
≪主制約≫
≪副制約群≫
≪構築子制約≫
≪主制約≫ , ≪副制約群≫
≪主制約≫ , ≪構築子制約≫
≪副制約群≫ , ≪構築子制約≫
≪主制約≫ , ≪副制約群≫ , ≪構築子制約≫
≪主制約≫:
≪クラス型≫
class
struct
≪副制約群≫:
≪インタフェース型≫
≪型仮引数≫
≪副制約群≫ , ≪インタフェース型≫
≪副制約群≫ , ≪型仮引数≫
≪構築子制約≫
new ( )
524
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
A.3 安全でないコードのための文法拡張
≪クラス修飾子≫:
...
unsafe
≪構造体修飾子≫:
...
unsafe
≪インタフェース修飾子≫:
...
unsafe
≪委譲修飾子≫:
...
unsafe
≪フィールド修飾子≫:
...
unsafe
≪メソッド修飾子≫:
...
unsafe
≪特性修飾子≫:
...
unsafe
≪イベント修飾子≫:
...
unsafe
≪添字子修飾子≫:
...
unsafe
≪演算子修飾子≫:
...
unsafe
≪構築子修飾子≫:
...
unsafe
≪終了化子宣言≫:
≪属性群≫opt externopt
unsafeopt ~ ≪識別子≫ ( ) ≪終了化子本体≫
≪属性群≫opt unsafeopt externopt ~ ≪識別子≫ ( ) ≪終了化子本体≫
≪静的構築子修飾子群≫:
externopt
unsafeopt static
525
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
unsafeopt externopt static
externopt
static unsafeopt
unsafeopt static externopt
static externopt
unsafeopt
static unsafeopt externopt
≪埋込み文≫:
...
≪安全でない文≫
≪安全でない文≫:
unsafe ≪ブロック≫
≪型≫:
≪値型≫
≪参照型≫
≪型仮引数≫
≪ポインタ型≫
≪ポインタ型≫:
≪非管理型≫ *
void *
≪非管理型≫:
≪型≫
≪一次非配列生成式≫:
...
≪sizeof式≫
≪一次非配列生成式≫:
...
≪ポインタメンバアクセス≫
≪ポインタ要素アクセス≫
≪単項式≫:
...
≪ポインタ間接参照式≫
≪アドレス参照式≫
≪ポインタ間接参照式≫:
* ≪単項式≫
≪ポインタメンバアクセス≫:
≪一次式≫ -> ≪識別子≫ ≪型実引数並び≫opt
≪ポインタ要素アクセス≫:
≪一次非配列生成式≫ [ ≪式≫ ]
≪アドレス参照式≫:
& ≪単項式≫
≪sizeof式≫:
526
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
sizeof ( ≪非管理型≫ )
≪埋込み文≫:
...
≪固定文≫
≪固定文≫:
fixed ( ≪ポインタ型≫ ≪固定ポインタ宣言子群≫ ) ≪埋込み文≫
≪固定ポインタ宣言子群≫:
≪固定ポインタ宣言子≫
≪固定ポインタ宣言子群≫ , ≪固定ポインタ宣言子≫
≪固定ポインタ宣言子≫:
≪識別子≫ = ≪固定ポインタ初期化子≫
≪固定ポインタ初期化子≫:
& ≪変数参照≫
≪式≫
≪変数初期化子≫:
≪式≫
≪配列初期化子≫
≪スタック割当て初期化≫
≪スタック割当て初期化≫:
stackalloc ≪非管理型≫ [ ≪式≫ ]
527
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
附属書B
(参考)
移植性の問題点
序文
この附属書は,本体の規定を補足するものであって,規定の一部ではない。
この附属書は規格本体に現れる移植性に関する情報をまとめる。
B.1
未定義の振る舞い
安全でない修飾子の出現を含まないプログラムは,未定義の振る舞いを示すことはない。
次の状況において振る舞いは,未定義である。
− あるポインタ型から別のポインタ型へ変換した結果の参照をたぐり,結果のポインタが指し示す型に
適切に境界調整されていない場合(27.4参照)。
− 単項演算子*が不当な値を保持するポインタに適用されている場合(27.5.1参照)。
− ポインタが範囲外の要素にアクセスするように添字付けされている場合(27.5.3参照)。
− 管理された型のオブジェクトを固定のポインタで修飾する(27.6参照)。
− stackallocによって割り当てられた記憶の初期内容(27.7参照)。
− stackallocによって負の数の領域を割り当てようとする試み(27.7参照)。
B.2
実装定義の振る舞い
規格適合処理系はB.2に一覧表示された個々の項目について選択した振る舞いを文書化しなければなら
ない。
− Unicodeの正規形Cでない識別子に遭遇したときの振る舞い(9.4.2参照)。
− ≪識別子又はキーワード≫を伴う≪行表示子≫において,≪識別子又はキーワード≫の値がdefault
と等しくないものの目的(9.5.7参照)。
− #pragma指令において≪前処理プラグマテキスト≫内の≪入力文字群≫の解釈(9.5.8参照)。
− ホスト環境によってアプリケーションの開始より前にMainに渡されたすべてのアプリケーション仮
引数の値(10.1参照)。
− 非検査文脈において,整数型割算の左オペランドがint又はlongの絶対値が最も大きな負数であり,
右オペランドが−1の場合に,System.ArithmeticException(又はそのサブクラス)が送出され
るのか,若しくはオーバフローが報告なしに発生するのかに関する選択(14.7.2参照)。
− 10進剰余演算実行中のどの時点でSystem.ArithmeticException(又はそのサブクラス)が送出
されるのか(14.7.3参照)。
− 非検査文脈にあり,整数除算の左演算対象が最大の負のint又はlong値で,かつ,右演算対象が−
1である場合に,System.ArithmeticException(又はそれの下位クラス)が送出されるか,けた
(桁)あふれは報告されずに結果値を左演算値とするか(17.5.7参照)。
− 外部関数との結合を達成する機構(17.5.7参照)。
− 例外と一致するcatch節が見つけられず,最初にそのスレッドを開始させたコードに到達したときの
スレッド終了の影響(23.3参照)。
528
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
− この規格で規定されたもの以外の属性指定定義の目的(24.2参照)。
− ポインタと整数との対応付け(27.4参照)。
− 単項演算子*をnullポインタに適用する効果(27.5.1参照)。
− ポインタ算術がポインタ型の定義域からけた(桁)あふれするときの振る舞い(27.5.5参照)。
− あらかじめ定義された値型以外に対するsizeof演算子の結果(27.5.8参照)。
− 配列式がnullか又は配列の要素がゼロの際のfixed文の振る舞い(27.6参照)。
− 文字列式がnullの際のfixed文の振る舞い(27.6参照)。
− 大きさゼロのスタックが割り当てられたときの返却値(27.7参照)。
B.3
未定義の動作
− オブジェクトが終了の候補となった後で,そのオブジェクトの終了化子(もしあれば)が実行される
時刻(10.9参照)。
− 検査されていない文脈において区間外の値をfloat値又はdouble値から整数型へ変換するときの
結果の値(13.2.1参照)。
− 安全でない文脈における場合を除く配列の配置(14.5.10.1参照)。
− ≪無名メソッド式≫の評価及び呼出しによる場合を除き,無名メソッドのブロックを実行する方法が
あるかどうか(14.5.15.2参照)。
− 単一の入口からなる≪無名メソッド式≫によって生成された委譲呼出しの並び。委譲の正確な対象オ
ブジェクト及び正確な対象メソッドは未定義とする(14.5.15.4参照)。
− 静的フィールドの初期化の厳密なタイミング(17.4.5.1参照)。
− 終了化子実行中に発生し,捕そく(捉)されない例外の動作(23.3参照)。
− 複数の部分で宣言され,その各部分の属性が,未定義の順序によって,結合されることによって決定
された型の属性(24.2参照)。
− メンバが構造体に詰め込まれる順序(27.5.8参照)。
− 列挙子オブジェクトが実行中状態にある場合,MoveNext呼出しの結果は未定義とする(26.2.1参照)。
− 列挙子オブジェクトが実行前,実行中,又は実行後の状態にある場合,Currentアクセスの結果は未
定義とする(26.2.2参照)。
− 列挙子オブジェクトが実行中状態にある場合,Dispose呼出しの結果は未定義とする(26.2.3参照)。
B.4
その他の問題点
浮動小数点式の評価の厳密な結果は実装ごとに変化する可能性がある。なぜなら実装はそのような式を
必す(須)のものより大きな区間及び/又は精度を用いて評価することが許されているからである(11.1.6
参照)。
CLIは幾つかの呼出し情報を他のプログラム言語との互換性のために予約する(17.2.7参照)。
529
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
附属書C
(参考)
名前付けの指針
序文
この附属書は,本体の規定を補足するものであって,規定の一部ではない。
この附属書の情報は,次に掲載されている。
http://msdn2.microsoft.com/en-us/library/czefa0ke(vs.71,printer).aspx
530
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
附属書D
(規定)
標準ライブラリ
適合したC#実装は,特定の意味規定をもつ型の最低限の集合を提供しなければならない。これらの型と
そのメンバを名前空間及び型についての英字順に一覧にする。これらの型及びそのメンバの正式の定義に
関しては,この規格の引用規格,ISO/IEC 23271:2005, Common Language Infrastructure (CLI) のPartition IV
にある“Base Class Library (BCL),Extended Numerics Library,及びExtended Array Library”に示されている。
注記 ISO/IEC 23271の以前の版(2003)は,JIS X 3016 (2006)として発行されている。
Systemで始まる型名は,標準ライブラリによって使用されることが意図されている。現在使われてい
ないそれらの名前は,規格の将来版で定義される可能性がある。
参考 次は,参考とする。
標準ライブラリは,適合したC#実装が必要とする型及びメンバの最小限の集合となるよう意図されてい
る。このため,それはC#言語規格が明示的に必要とするそれらの型及びメンバだけを含んでいる。
適合したC#実装は,有用なプログラムを書くことを可能とする非常に多くの,より豊富なライブラリを
提供することを期待されている。例えば,適合した実装は,本ライブラリを次のように拡張してもよい。
− 名前空間を追加する。
− 型を追加する。
− インタフェース型以外のメンバを追加する。
− 付加的なインタフェースを実装する構造体及びクラス型をもつ。
− 既存の型及びメンバに対して(ConditionalAttribute以外の)属性を追加する。
参考 終わり。
namespace System
{
public class ApplicationException : Exception
{
public ApplicationException();
public ApplicationException(string message);
public ApplicationException(string message, Exception
innerException);
}
}
namespace System
{
public class ArgumentException : SystemException
{
public ArgumentException();
531
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public ArgumentException(string message);
public ArgumentException(string message, Exception
innerException);
}
}
namespace System
{
public class ArithmeticException : SystemException
{
public ArithmeticException();
public ArithmeticException(string message);
public ArithmeticException(string message, Exception
innerException);
}
}
namespace System
{
public abstract class Array : IList, ICollection, IEnumerable
{
public int Length { get; }
public int Rank { get; }
public int GetLength(int dimension);
}
}
namespace System
{
public class ArrayTypeMismatchException : SystemException
{
public ArrayTypeMismatchException();
public ArrayTypeMismatchException(string message);
public ArrayTypeMismatchException(string message,
Exception innerException);
}
}
namespace System
{
532
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
[AttributeUsageAttribute(AttributeTargets.All, Inherited = true,
AllowMultiple = false)]
public abstract class Attribute
{
protected Attribute();
}
}
namespace System
{
public enum AttributeTargets
{
Assembly = 1,
Module = 2,
Class = 4,
Struct = 8,
Enum = 16,
Constructor = 32,
Method = 64,
Property = 128,
Field = 256,
Event = 512,
Interface = 1024,
Parameter = 2048,
Delegate = 4096,
ReturnValue = 8192,
GenericParameter = 16384,
All = 32767
}
}
namespace System
{
[AttributeUsageAttribute(AttributeTargets.Class, Inherited =
true)]
public sealed class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets validOn);
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
533
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public AttributeTargets ValidOn { get; }
}
}
namespace System
{
public struct Boolean
{
}
}
namespace System
{
public struct Byte
{
}
}
namespace System
{
public struct Char
{
}
}
namespace System
{
public struct Decimal
{
}
}
namespace System
{
public abstract class Delegate
{
}
}
namespace System
534
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public class DivideByZeroException : ArithmeticException
{
public DivideByZeroException();
public DivideByZeroException(string message);
public DivideByZeroException(string message, Exception
innerException);
}
}
namespace System
{
public struct Double
{
}
}
namespace System
{
public abstract class Enum : ValueType
{
protected Enum();
}
}
namespace System
{
public class Exception
{
public Exception();
public Exception(string message);
public Exception(string message, Exception innerException);
public sealed Exception InnerException { get; }
public virtual string Message { get; }
}
}
namespace System
{
public interface IDisposable
535
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public void Dispose();
}
}
namespace System
{
public sealed class IndexOutOfRangeException : SystemException
{
public IndexOutOfRangeException();
public IndexOutOfRangeException(string message);
public IndexOutOfRangeException(string message,
Exception innerException);
}
}
namespace System
{
public struct Int16
{
}
}
namespace System
{
public struct Int32
{
}
}
namespace System
{
public struct Int64
{
}
}
namespace System
{
public class InvalidCastException : SystemException
536
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public InvalidCastException();
public InvalidCastException(string message);
public InvalidCastException(string message, Exception
innerException);
}
}
namespace System
{
public class InvalidOperationException : SystemException
{
public InvalidOperationException();
public InvalidOperationException(string message);
public InvalidOperationException(string message,
Exception innerException);
}
}
namespace System
{
public abstract class MemberInfo
{
protected MemberInfo();
}
}
namespace System
{
public class NotSupportedException : SystemException
{
public NotSupportedException();
public NotSupportedException(string message);
public NotSupportedException(string message, Exception
innerException);
}
}
namespace System
{
537
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public struct Nullable<T>
{
public bool HasValue { get; }
public T Value { get; }
}
}
namespace System
{
public class NullReferenceException : SystemException
{
public NullReferenceException();
public NullReferenceException(string message);
public NullReferenceException(string message, Exception
innerException);
}
}
namespace System
{
public class Object
{
public Object();
~Object();
public virtual bool Equals(object obj);
public virtual int GetHashCode();
public Type GetType();
public virtual string ToString();
}
}
namespace System
{
[AttributeUsageAttribute(AttributeTargets.Class
| AttributeTargets.Struct
| AttributeTargets.Enum | AttributeTargets.Interface
| AttributeTargets.Constructor |
AttributeTargets.Method
| AttributeTargets.Property |
AttributeTargets.Field
538
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
| AttributeTargets.Event | AttributeTargets.Delegate,
Inherited = false)]
public sealed class ObsoleteAttribute : Attribute
{
public ObsoleteAttribute();
public ObsoleteAttribute(string message);
public ObsoleteAttribute(string message, bool error);
public bool IsError { get; }
public string Message { get; }
}
}
namespace System
{
public class OutOfMemoryException : SystemException
{
public OutOfMemoryException();
public OutOfMemoryException(string message);
public OutOfMemoryException(string message, Exception
innerException);
}
}
namespace System
{
public class OverflowException : ArithmeticException
{
public OverflowException();
public OverflowException(string message);
public OverflowException(string message, Exception
innerException);
}
}
namespace System
{
public struct SByte
{
}
}
539
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
namespace System
{
public struct Single
{
}
}
namespace System
{
public sealed class StackOverflowException : SystemException
{
public StackOverflowException();
public StackOverflowException(string message);
public StackOverflowException(string message, Exception
innerException);
}
}
namespace System
{
public sealed class String : IEnumerable<Char>, IEnumerable
{
public int Length { get; }
public char this[int index] { get; }
}
}
namespace System
{
public class SystemException : Exception
{
public SystemException();
public SystemException(string message);
public SystemException(string message, Exception
innerException);
}
}
namespace System
540
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public abstract class Type : MemberInfo
{
}
}
namespace System
{
public sealed class TypeInitializationException : SystemException
{
public TypeInitializationException(string fullTypeName,
Exception innerException);
}
}
namespace System
{
public struct UInt16
{
}
}
namespace System
{
public struct UInt32
{
}
}
namespace System
{
public struct UInt64
{
}
}
namespace System
{
public abstract class ValueType
{
541
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
protected ValueType();
}
}
namespace System.Collections
{
public interface ICollection : IEnumerable
{
public int Count { get; }
public bool IsSynchronized { get; }
public object SyncRoot { get; }
public void CopyTo(Array array, int index);
}
}
namespace System.Collections
{
public interface IEnumerable
{
public IEnumerator GetEnumerator();
}
}
namespace System.Collections
{
public interface IEnumerator
{
public object Current { get; }
public bool MoveNext();
public void Reset();
}
}
namespace System.Collections
{
public interface IList : ICollection, IEnumerable
{
public bool IsFixedSize { get; }
public bool IsReadOnly { get; }
542
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public object this[int index] { get; set; }
public int Add(object value);
public void Clear();
public bool Contains(object value);
public int IndexOf(object value);
public void Insert(int index, object value);
public void Remove(object value);
public void RemoveAt(int index);
}
}
namespace System.Collections.Generic
{
public interface ICollection<T> : IEnumerable<T>
{
public int Count { get; }
public bool IsReadOnly { get; }
public void Add(T item);
public void Clear();
public bool Contains(T item);
public void CopyTo(T[] array, int arrayIndex);
public bool Remove(T item);
}
}
namespace System.Collections.Generic
{
public interface IEnumerable<T> : IEnumerable
{
public IEnumerator<T> GetEnumerator();
}
}
namespace System.Collections.Generic
{
public interface IEnumerator<T> : IDisposable, IEnumerator
{
public T Current { get; }
}
}
543
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
namespace System.Collections.Generic
{
public interface IList<T> : ICollection<T>
{
public T this[int index] { get; set; }
public int IndexOf(T item);
public void Insert(int index, T item);
public void RemoveAt(int index);
}
}
namespace System.Diagnostics
{
[AttributeUsageAttribute(AttributeTargets.Method
| AttributeTargets.Class, AllowMultiple = true)]
public sealed class ConditionalAttribute : Attribute
{
public ConditionalAttribute(string conditionString);
public string ConditionString { get; }
}
}
namespace System.Threading
{
public static class Monitor
{
public static void Enter(object obj);
public static void Exit(object obj);
}
}
544
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
附属書E
(参考)
文書化注釈
序文
この附属書は,本体の規定を補足するものであって,規定の一部ではない。
C#には,XMLテキストを含む特殊な注釈構文を用いてコードを文書化するためのプログラマ向けの機
構が用意されている。このような構文を使用する注釈は文書化注釈と呼ばれる。XML生成ツールは文書生
成器という(C#処理系が生成器になってもよいが,その必要はない。)。文書生成器によって作り出された
出力は文書ファイルと呼ばれる。文書ファイルは文書閲覧器の入力となる。文書閲覧器は,型情報及び関
連文書を見やすいように提示する。
C#適合処理系は,文書化注釈の構文を検査するようには要求されていない。このような注釈は,普通の
注釈である。しかし,処理系がこのような検査をすることも許される。
この規格では,文書化注釈で利用する標準的なタグ集合を推奨する。CLI (Common Language
Infrastructure) を目標としたC#の実装に関して,文書生成器についての情報と文書ファイルの書式を提供
する。文書閲覧器についての情報は提供しない。
E.1
概要
特別な形式の注釈を使用すると,注釈とその直後にあるソースコード要素からXMLを生成するように
ツールに指示できる。そのような注釈は///...形式の単一行注釈であるか又は/**...*/形式の区切り注
釈とする。文書化注釈は,注記する利用者定義型(クラス,委譲,インタフェースなど)又はメンバ(フ
ィールド,イベント,特性,メソッドなど)の直前に指定する必要がある。属性節は宣言部と考えられる
ため,文書化注釈は,型又はメンバに適用される属性の前に定義されなければならない。
構文
≪単一行文書化注釈≫::
/// ≪入力文字群≫opt
≪区切り文書化注釈≫::
/** ≪区切り注釈文字群≫opt */
≪単一行文書化注釈≫では,現在の≪単一行文書化注釈≫に隣接する各≪単一行文書化注釈≫の///文
字の直後に≪空白類≫がある場合,その≪空白類≫はXML出力に含まれない。
≪区切り文書化注釈≫では,2行目の最初の≪空白類≫以外の文字が≪星印≫で,≪区切り文書化注釈
≫内の各行の先頭にある0個以上の≪空白類≫と1個の≪星印≫が同じパターンで繰り返される場合,繰
返しパターンの文字はXML出力に含まれない。パターンの≪星印≫の前後には,≪空白類≫を挿入でき
る。
例
/**
* <remarks>
* Class <c>Point</c> models a point in a two-dimensional plane.
* </remarks>
545
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
*/
public class Point
{
/// <remarks>Method <c>Draw</c> renders the point.</remarks>
void Draw() { … }
}
文書化注釈内のテキストは,XMLの規則に従って適格である必要がある
(http://www.w3.org/TR/REC-xml)。不適格なXMLの場合は警告が表示され,文書ファイルにはエラーが発
生したことを表す注釈が記録される。
開発者は,自分用のタグ集合を自由に作成してかまわないが,E.2では推奨タグを定義する。推奨され
るタグには,次のような特殊な意味をもつタグがある。
− <param>タグは,仮引数の記述に使用する。このタグが使用されている場合,文書生成器は,指定さ
れた仮引数が存在することと,すべての仮引数が文書化注釈内に記述されていることを検証する必要
がある。検証が失敗すると,文書生成器は警告を通知する。
− 属性crefは,任意のタグに付けてコード要素への参照ができる。文書生成器は,該当するコード要
素が存在することを検証する必要がある。検証が失敗すると,文書生成器は警告を通知する。属性
crefで記述された名前を検索する場合,文書生成器は,ソースコードに出現するusing文に従った
名前空間の可視性を尊重しなければならない。コード要素が総称性を利用するコードを含む場合,例
えばList<T>のような通常の総称性構文は,無効なXMLを生成するため使用できない。その代わり
に,中括弧を用いて,<see cref=”List{T}”/>のように記述すること,又は通常のXML拡張表記
によって,例えばList<T>中で<及び>と記述することができる。総称型のメンバは,
List{T}.Add(T)のように参照する。Add(T)における変数Tは,List{T}の型仮引数Tに束縛され
ている。同様に,総称メソッドSort{T}(T[])における仮引数型Tは,型仮引数Tに束縛されてい
る。一般に,総称クラス内の総称メソッドは,C{S}.F{T}(S, List{T}, U)のように参照する。こ
こで,仮引数SはクラスC{S}の型仮引数に束縛されている。List(T)のTは,総称メソッドF{T}
の型仮引数に束縛されている。仮引数型Uは束縛されていない。
− <summary>タグは,文書閲覧器が型又はメンバに関して追加情報を表示するためのものとする。
文書ファイルでは,型及びメンバに関する完全な情報は提供されない。例えば,文書ファイルには型情
報が含まれていない。型及びメンバの完全な情報を得るには,文書ファイルを実行時型及びメンバの自己
反映とともに使用する必要がある。
E.2
推奨タグ
文書生成器は,XMLの規則に従って,有効なタグをすべて受け入れて処理する必要がある。使用者の文
書で一般的に使用される機能を提供するタグを次の表に示す。この表に示した以外のタグも使用できる。
タグ
参照
目的
<c>
E.2.1
テキストをコード形式のフォントに設定する。
<code>
E.2.2
1 行以上のソースコード又はプログラム出力を設定する。
<example>
E.2.3
例を示す。
<exception>
E.2.4
メソッドが送出する例外を示す。
<list>
E.2.5
並び又は表を生成する。
<para>
E.2.6
テキストに構造体を追加できる。
546
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
タグ
参照
目的
<param>
E.2.7
メソッド又は構築子の仮引数について説明する。
<paramref>
E.2.8
語が仮引数名であることを識別する。
<permission>
E.2.9
メンバのセキュリティ アクセス可能性を示す。
<remarks>
E.2.10
型について説明する。
<returns>
E.2.11
メソッドの返却値について説明する。
<see>
E.2.12
リンクを指定する。
<seealso>
E.2.13
“参照”の入口を生成する。
<summary>
E.2.14
型のメンバについて説明する。
<typeparam>
E.2.15
総称型又は総称メソッドの型仮引数について説明する。
<typeparamref>
E.2.16
語句が,型仮引数名であることを識別する。
<value>
E.2.15
特性について説明する。
E.2.1 <c>
このタグは,記述内のテキストの一部を,コードブロックで使用される特定のフォントに設定する必要
があることを示す。実コードでは<code>を使う(E.2.2参照)。
構文
<c>text to be set like code</c>
例
/// <remarks>
/// Class <c>Point</c> models a point in a two-dimensional plane.
/// </remarks>
public class Point
{
…
}
E.2.2 <code>
このタグは,1行以上のソースコード又はプログラム出力を特殊フォントに設定する場合に使用する。
文中のコード断片には,<c>を使う(E.2.1参照)。
構文
<code>source code or program output</code>
例
/// <summary>
/// Changes the Point's location by the given x- and y-offsets.
/// </summary>
/// <example>
/// The following code:
/// <code>
///
Point p = new Point(3,5);
///
p.Translate(-1,3);
/// </code>
/// results in <c>p</c>'s having the value (2,8).
547
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
/// </example>
public void Translate(int xor, int yor) {
X += xor;
Y += yor;
}
E.2.3 <example>
このタグを使用すると,注釈内にプログラム例を挿入して,メソッドや他のライブラリメンバの使用方
法を指定できる。通常,これは<code>(E.2.2参照)タグも使用する。
構文
<example>description</example>
例
<code> (E.2.2) の例を参照。
E.2.4 <exception>
このタグを使用すると,メソッドが送出できる例外を示すことができる。
構文
<exception cref="member">description</exception>
指定項目
cref="member"
メンバの名前。文書生成器は,指定されたメンバが存在するかどうかを検査し,memberを文書ファイ
ル内の標準要素名に変換する。
description
例外が送出される状況についての記述である。
例
public class DataBaseOperations
{
/// <exception
cref="MasterFileFormatCorruptException"></exception>
/// <exception cref="MasterFileLockedOpenException"></exception>
public static void ReadRecord(int flag) {
if (flag == 1)
throw new MasterFileFormatCorruptException();
else if (flag == 2)
throw new MasterFileLockedOpenException();
// …
}
}
E.2.5 <list>
このタグを用いて,項目の並び又は表を生成する。これは,表又は定義並びの見出しを定義する
<listheader>区分を含んでよい(表定義の場合,見出しのtermの内容さえ与えればよい。)。
並びの各項目は,<item>ブロックで指定する。定義並びを生成する場合は,term及びdescription
548
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
の両方を規定しなければならない。しかし,表及び番号付き又は番号のない箇条書きでは,description
さえ指定すればよい。
構文
<list type="style">
<listheader>
<term>term</term>
<description>description</description>
</listheader>
<item>
<term>term</term>
<description>description</description>
</item>
…
<item>
<term>term</term>
<description>description</description>
</item>
</list>
指定項目
style
並びの形式。bullet, number,又はtableのいずれかでなければならない。
term
定義がdescriptionで与えられた項目。
description
行頭文字若しくは番号付き並びの項目,又は,termの定義のいずれかとする。
例
public class MyClass
{
/// <remarks>
/// Here is an example of a bulleted list:
/// <list type="bullet">
/// <item>
/// <description>First item.</description>
/// </item>
/// <item>
/// <description>Second item.</description>
/// </item>
/// </list>
/// </remarks>
public static void Main () {
549
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
…
}
}
E.2.6 <para>
このタグは,<remarks>(E.2.10参照)又は<returns>(E.2.11参照)のような他のタグの内部で使
われ,構造部分にテキストを付加することを可能にする。
構文
<para>content</para>
指定項目
content
段落のテキスト。
例
/// <summary>
/// <para>
/// This is the entry point of the Point class testing program.
/// </para>
/// <para>
/// This program tests each method and operator, and is intended
/// to be run after any non-trvial maintenance has been performed
/// on the Point class.
/// </para>
/// </summary>
public static void Main() {
…
}
E.2.7 <param>
このタグを用いて,メソッド,構築子又は添字子の仮引数について説明する。
構文
<param name="name">description</param>
指定項目
名前
仮引数の名前。
description
仮引数の記述。
例
/// <summary>
/// This method changes the Point's location to the given coordinates.
/// </summary>
/// <param name="xor">The new x-coordinate.</param>
/// <param name="yor">The new y-coordinate.</param>
550
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
public void Move(int xor, int yor) {
X = xor;
Y = yor;
}
E.2.8 <paramref>
このタグを用いて,名前が仮引数であることを示す。文書ファイルを処理するときに,この仮引数に対
して独立した書式を設定できる。
構文
<paramref name="name"/>
指定項目
名前
仮引数の名前。
例
/// <summary>
/// This constructor initializes the new Point to
///
(<paramref name="xor"/>,<paramref name="yor"/>).
/// </summary>
/// <param name="xor">The new Point's x-coordinate.</param>
/// <param name="yor">The new Point's y-coordinate.</param>
public Point(int xor, int yor) {
X = xor;
Y = yor;
}
E.2.9 <permission>
このタグを使用すると,メンバのセキュリティアクセスの程度を文書化する。
構文
<permission cref="member">description</permission>
指定項目
"member"
メンバの名前。文書生成器は,指定されたコード要素が存在するかどうかを検査し,memberを文書フ
ァイル内の標準要素名に変換する。
description
メンバへのアクセスの記述。
例
/// <permission cref="System.Security.PermissionSet">
/// Everyone can access this method.
/// </permission>
public static void Test() {
…
}
551
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
E.2.10 <remarks>
このタグを用いて,型の概要情報を指定する。型のメンバを記述するには,<summary>を使う(E.2.14
参照)。
構文
<remarks>text</remarks>
指定項目
text
解説のテキスト。
例
/// <remarks>
/// Class <c>Point</c> models a point in a two-dimensional plane.
/// </remarks>
public class Point
{
…
}
E.2.11 <returns>
このタグを用いて,メソッドの返却値について説明する。
構文
<returns>description</returns>
指定項目
description
返却値の記述。
例
/// <summary>
/// Report a Point's location as a string.
/// </summary>
/// <returns>
/// A string representing a Point's location, in the form (x,y),
///
without any leading, training, or embedded whitespace.
/// </returns>
public override string ToString() {
return "(" + X + "," + Y + ")";
}
E.2.12 <see>
このタグを使用すると,テキスト内にリンクを指定できる。“をも参照”箇所の文章を指す場合には,
<seealso>を使う(E.2.13参照)。
構文
<see cref="member"/>
指定項目
552
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
member
メンバの名前。文書生成器は,与えられたコード要素が存在するか検査して,memberに文書ファイル
の要素名を渡す。
例
/// <summary>
/// This method changes the Point's location to the given coordinates.
/// Use the <see cref="Translate"/> method to apply a relative change.
/// </summary>
public void Move(int xor, int yor) {
X = xor;
Y = yor;
}
/// <summary>
/// This method changes the Point's location by the given offsets.
/// Use the <see cref="Move"/> method to directly set the coordinates.
/// </summary>
public void Translate(int xor, int yor) {
X += xor;
Y += yor;
}
E.2.13 <seealso>
このタグを使用すると,“をも参照”節の入口を生成できる。テキスト内からのリンクを張るには<see>
を使う(E.2.12参照)。
構文
<seealso cref="member"/>
指定項目
member
メンバの名前。文書生成器は,与えられたコード要素が存在するか検査して,memberに文書ファイル
の要素名を渡す。
例
/// <summary>
/// This method determines whether two Points have the same location.
/// </summary>
/// <seealso cref="operator =="/>
/// <seealso cref="operator !="/>
public override bool Equals(object o) {
…
}
E.2.14 <summary>
このタグを使用すると,型のメンバについて説明できる。型そのものを記述するには,<remarks>を使
553
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
う(E.2.10参照)。
構文
<summary>description</summary>
指定項目
description
メンバの概要。
例
/// <summary>
/// This constructor initializes the new Point to (0,0).
/// </summary>
public Point(): this(0,0) {
}
E.2.15 <typeparam>
このタグを使用すると,総称型又は総称メソッドに対する型仮引数を説明できる。
構文
<typeparam name=”name”>description</typeparam>
指定項目
name
型仮引数の名前。
description
仮引数の記述。
例
/// <summary>
/// This method creates a new array of arbitrary type.
/// </summary>
/// <typeparam name="T">The element type of the array</typeparam>
public static T[] MakeArray<T>(int n) {
return new T[n];
}
E.2.16 <typeparamref>
このタグを使用すると,語句が型仮引数であることを説明できる。文書ファイルでは,本仮引数を何ら
かの区別された様式に書式化するために使用することができる。
構文
<typeparamref name=”name”/>
指定項目
name
仮引数の名前。
例
/// <summary>
/// This method creates a new array of arbitrary type
554
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
///
<typeparamref name=”T”/>
/// </summary>
/// <typeparam name="T">The element type of the array</typeparam>
public static T[] MakeArray<t>(int n) {
return new T[n];
}
E.2.17 <value>
このタグを使用すると,特性について説明できる。
構文
<value>property description</value>
指定項目
property description
特性の記述。
例
/// <value>
/// The point's x-coordinate.
/// </value>
public int X
{
get { return x; }
set { x = value; }
}
E.3
文書ファイルの処理
次の情報は,CLIを対象としたC#実装のためとする。
文書生成器は,文書化注釈でタグ付けされたソースコード内の要素ごとに,ID文字列を生成する。この
ID文字列は,ソース要素を一意に識別する。文書閲覧器は,ID文字列を用いて,文書の適用対象となる
メタデータ及び自己反映項目を識別できる。
文書ファイルは,ソースコードの階層的表現ではなく,要素ごとに生成されたID文字列を平たん(坦)
な並びで表す。
E.3.1 ID文字列書式
文書生成器は,次の規則に基づいてID文字列を生成する。
− 生成される文字列に空白類は含まれない。
− 文字列の先頭部分は,記述されるメンバの種類を,単一文字及びコロンを用いて識別する。定義され
るメンバの種類は,次のとおりとする。
555
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
種類
記述
E
イベント
F
フィールド
M
メソッド(構築子,終了化子,演算子を含む)
N
名前空間
P
特性(添字子を含む)
T
型(クラス,委譲,enum,インタフェース,構造
体など)
!
エラー文字列。エラーに続く文字列で,エラーの内
容を示す。例えば,文書生成器は,解決できないリ
ンクについてのエラー情報を生成する。
− 文字列の2番目の部分は,要素の完全限定名となる。名前は,名前空間の根から始まる。要素の名前,
要素が含まれている型及び名前空間は,ピリオドで区切られる。項目そのものの名前にピリオドが含
まれている場合には,# (U+0023)で置き換える(要素名には,#が含まれていないことが前提になっ
ている。)。
− メソッド又は特性の実引数がある場合は,括弧で囲まれた実引数並びが続く。実引数がない場合は,
括弧が省略される。実引数の区切り文字には,コンマを使用する。各実引数の符号化は,CLI呼出し
情報と同じとする。このため,実引数は,完全限定名を基とする,完全な文書名で表現する。例えば,
intはSystem.Int32,stringはSystem.String,objectはSystem.Objectなどとなる。
総称型仮引数を定義する実引数は,低アクセント文字“̀”に引き続く型仮引数の個数が付加され,例
えばC̀1のようになる。入れ子にされた型においてその数は,入れ子にされた型の新しい型仮引数を
基とし,例えばC̀1.NestedC̀2となる。修飾子out又はrefをもつ実引数は,型名の後に“@”が
続く。値渡し又はparamsによる実引数は,特別な記法をもたない。配列の実引数は,[下限:大きさ,
…,下界:大きさ]で表現される。ここで,コンマの個数は,次元数‒ 1となり,下界及び大きさはそれ
ぞれの次元で分かっていれば10進数で示される。下界又は大きさが規定されていない場合は,省略さ
れる。ある次元で下界及び大きさが省略される場合は,その次元の“:”も省略される。非長方形配列
は,レベルごとに“[ ]”で表現される。void以外のポインタ型の実引数は,型名の後に“*”を続け
て表す。voidポインタは,“System.Void”の型名を用いて表す。型において総称型仮引数を参照
する実引数は,単一の低アクセント文字“̀”に引き続き,ゼロを初期値とする型仮引数の添字を用い
てコード化される。メソッドにおいて総称仮引数を使用する実引数は,型の仮引数が単一の低アクセ
ント文字を用いるのに対し,二重の低アクセント文字“̀̀”に引き続き,ゼロを初期値とする型仮引
数の添字を用いる。構築総称型を参照する実引数は,総称型に引き続き,中括弧“{”と“}”で囲ま
れたコンマ区切りの型実引数の並びを用いてコード化される。
E.3.2 ID文字列例
次のコード例は,C#のコードの一部,及び,文書化注釈をもつことができる各ソース要素から生成され
たID文字列を示す。
− 型は,完全限定名で表現される。
enum Color { Red, Blue, Green }
namespace Acme
{
interface IProcess { … }
556
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
struct ValueType { … }
class Widget: IProcess
{
public class NestedClass { … }
public interface IMenuItem { … }
public delegate void Del(int i);
public enum Direction { North, South, East, West }
}
class MyList<T>
{
class Helper<U,V>{ … }
}
}
"T:Color"
"T:Acme.IProcess"
"T:Acme.ValueType"
"T:Acme.Widget"
"T:Acme.Widget.NestedClass"
"T:Acme.Widget.IMenuItem"
"T:Acme.Widget.Del"
"T:Acme.Widget.Direction"
"T:Acme.MyList̀1"
"T:Acme.MyList̀1.Helper̀2"
− フィールドは,完全限定名で表現される。
namespace Acme
{
struct ValueType
{
private int total;
}
class Widget: IProcess
{
public class NestedClass
{
private int value;
}
private string message;
private static Color defaultColor;
private const double PI = 3.14159;
protected readonly double monthlyAverage;
557
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
private long[] array1;
private Widget[,] array2;
private unsafe int *pCount;
private unsafe float **ppValues;
}
}
"F:Acme.ValueType.total"
"F:Acme.Widget.NestedClass.value"
"F:Acme.Widget.message"
"F:Acme.Widget.defaultColor"
"F:Acme.Widget.PI"
"F:Acme.Widget.monthlyAverage"
"F:Acme.Widget.array1"
"F:Acme.Widget.array2"
"F:Acme.Widget.pCount"
"F:Acme.Widget.ppValues"
− 構築子
namespace Acme
{
class Widget: IProcess
{
static Widget() { … }
public Widget() { … }
public Widget(string s) { … }
}
}
"M:Acme.Widget.#cctor"
"M:Acme.Widget.#ctor"
"M:Acme.Widget.#ctor(System.String)"
− 終了化子
namespace Acme
{
class Widget: IProcess
{
~Widget() { … }
}
}
"M:Acme.Widget.Finalize"
− メソッド
namespace Acme
558
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
struct ValueType
{
public void M(int i) { … }
}
class Widget: IProcess
{
public class NestedClass
{
public void M(int i) { … }
}
public static void M0() { … }
public void M1(char c, out float f, ref ValueType v) { … }
public void M2(short[] x1, int[,] x2, long[][] x3) { … }
public void M3(long[][] x3, Widget[][,,] x4) { … }
public unsafe void M4(char *pc, Color **pf) { … }
public unsafe void M5(void *pv, double *[][,] pd) { … }
public void M6(int i, params object[] args) { … }
}
class MyList<T>
{
public void Test(T t) { … }
}
class UseList
{
public void Process(MyList<int> list) { … }
public MyList<T> GetValues<T>(T value) { … }
}
}
"M:Acme.ValueType.M(System.Int32)"
"M:Acme.Widget.NestedClass.M(System.Int32)"
"M:Acme.Widget.M0"
"M:Acme.Widget.M1(System.Char,System.Single@,Acme.ValueType@)"
"M:Acme.Widget.M2(System.Int16[],System.Int32[0:,0:],System.Int64[
][])"
"M:Acme.Widget.M3(System.Int64[][],Acme.Widget[0:,0:,0:][])"
"M:Acme.Widget.M4(System.Char*,Color**)"
"M:Acme.Widget.M5(System.Void*,System.Double*[0:,0:][])"
"M:Acme.Widget.M6(System.Int32,System.Object[])"
"M:Acme.MyList̀1.Test(̀0)"
559
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
"M:Acme.UseList.Process(Acme.MyList{System.Int32})"
"M:Acme.UseList.getValues̀1(̀̀0)"
− 特性及び添字子
namespace Acme
{
class Widget: IProcess
{
public int Width {get { … } set { … }}
public int this[int i] {get { … } set { … }}
public int this[string s, int i] {get { … } set { … }}
}
}
"P:Acme.Widget.Width"
"P:Acme.Widget.Item(System.Int32)"
"P:Acme.Widget.Item(System.String,System.Int32)"
− イベント
namespace Acme
{
class Widget: IProcess
{
public event Del AnEvent;
}
}
"E:Acme.Widget.AnEvent"
− 単項演算子
namespace Acme
{
class Widget: IProcess
{
public static Widget operator+(Widget x) { … }
}
}
"M:Acme.Widget.op̲UnaryPlus(Acme.Widget)"
単項演算子に使われるすべての名前は次となる。op̲UnaryPlus,op̲UnaryNegation,
op̲LogicalNot,op̲OnesComplement,op̲Increment,op̲Decrement,op̲True及び
op̲False。
− 2項演算子
namespace Acme
{
class Widget: IProcess
560
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
{
public static Widget operator+(Widget x1, Widget x2) { return
x1; }
}
}
"M:Acme.Widget.op̲Addition(Acme.Widget,Acme.Widget)"
2項演算子に使われるすべての名前は次となる。op̲Addition,op̲Subtraction,op̲Multiply,
op̲Division,op̲Modulus,op̲BitwiseAnd,op̲BitwiseOr,op̲ExclusiveOr,
op̲LeftShift,op̲RightShift,op̲Equality,op̲Inequality,op̲LessThan,
op̲LessThanOrEqual,op̲GreaterThan及びop̲GreaterThanOrEqual。
− 変換演算子は,“~”の後に返却型が続く。
namespace Acme
{
class Widget: IProcess
{
public static explicit operator int(Widget x) { … }
public static implicit operator long(Widget x) { … }
}
}
"M:Acme.Widget.op̲Explicit(Acme.Widget)~System.Int32"
"M:Acme.Widget.op̲Implicit(Acme.Widget)~System.Int64"
E.4
例
E.4.1 C#ソースコード
次の例はクラスPointのソースコードを示す。
namespace Graphics
{
/// <remarks>
/// Class <c>Point</c> models a point in a two-dimensional plane.
/// </remarks>
public class Point
{
/// <summary>
/// Instance variable <c>x</c> represents the Point's x-coordinate.
/// </summary>
private int x;
/// <summary>
/// Instance variable <c>y</c> represents the Point's y-coordinate.
/// </summary>
private int y;
561
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
/// <value>
/// The Point's x-coordinate.
/// </value>
public int X
{
get { return x; }
set { x = value; }
}
/// <value>
/// The Point's y-coordinate.
/// </value>
public int Y
{
get { return y; }
set { y = value; }
}
/// <summary>
/// This constructor initializes the new Point to (0,0).
/// </summary>
public Point(): this(0,0) {}
/// <summary>
/// This constructor initializes the new Point to
/// (<paramref name="xor"/>,<paramref name="yor"/>).
/// </summary>
/// <param name="xor">The new Point's x-coordinate.</param>
/// <param name="yor">The new Point's y-coordinate.</param>
public Point(int xor, int yor) {
x = xor;
y = yor;
}
/// <summary>
/// This method changes the point's location to the given
/// coordinates.
/// </summary>
/// <param name="xor">The new x-coordinate.</param>
/// <param name="yor">The new y-coordinate.</param>
/// <seealso cref="Translate"/>
public void Move(int xor, int yor) {
x = xor;
y = yor;
562
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
}
/// <summary>
/// This method changes the point's location by the given
/// x- and y-offsets.
/// </summary>
/// <example>
/// The following code:
/// <code>
/// Point p = new Point(3,5);
/// p.Translate(-1,3);
/// </code>
/// results in <c>p</c>'s having the value (2,8).
/// </example>
/// <param name="xor">The relative x-offset.</param>
/// <param name="yor">The relative y-offset.</param>
/// <seealso cref="Move"/>
public void Translate(int xor, int yor) {
x += xor;
y += yor;
}
/// <summary>
/// This method determines whether two Points have the same
/// location.
/// </summary>
/// <param name="o">
/// The object to be compared to the current object.
/// </param>
/// <returns>
/// True if the Points have the same location; otherwise, false.
/// </returns>
/// <seealso cref="operator =="/>
/// <seealso cref="operator !="/>
public override bool Equals(object o) {
Point p = o as Point;
if (p == null) return false;
return x == p.x && y == p.y;
}
/// <summary>
/// Computes the hash code for a Point.
/// </summary>
563
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
/// <returns>
/// A hash code computed from the x and y coordinates.
/// </returns>
public override int GetHashCode() {
return x ^ y;
}
/// <summary>
/// Report a point's location as a string.
/// </summary>
/// <returns>
/// A string representing a point's location, in the form (x,y),
/// without any leading, training, or embedded whitespace.
/// </returns>
public override string ToString() {
return "(" + x + "," + y + ")";
}
/// <summary>
/// This operator determines whether two Points have the same
/// location.
/// </summary>
/// <param name="p1">The first Point to be compared.</param>
/// <param name="p2">The second Point to be compared.</param>
/// <returns>
/// True if the Points have the same location; otherwise, false.
/// </returns>
/// <seealso cref="Equals"/>
/// <seealso cref="operator !="/>
public static bool operator ==(Point p1, Point p2) {
if ((object)p1 == null || (object)p2 == null) return false;
return p1.x == p2.x && p1.y == p2.y;
}
/// <summary>
/// This operator determines whether two Points have the same
/// location.
/// </summary>
/// <param name="p1">The first Point to be compared.</param>
/// <param name="p2">The second Point to be compared.</param>
/// <returns>
/// True if the Points do not have the same location;
/// otherwise, false.
564
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
/// </returns>
/// <seealso cref="Equals"/>
/// <seealso cref="operator =="/>
public static bool operator !=(Point p1, Point p2) {
return !(p1 == p2);
}
/// <summary>
/// <para>
/// This is the entry point of the Point class testing program.
/// </para>
/// <para>
/// This program tests each method and operator, and is intended
/// to be run after any non-trvial maintenance has been performed
/// on the Point class.
/// </para>
/// </summary>
public static void Main() {
// class test code goes here
}
}
}
E.4.2 結果のXML
E.4.1に示したクラスPointのソースコードに対して文書生成器の出力は,次のとおりとなる。
<?xml version="1.0"?>
<doc>
<assembly>
<name>Point</name>
</assembly>
<members>
<member name="T:Graphics.Point">
<remarks>
Class <c>Point</c> models a point in a two-dimensional plane.
</remarks>
</member>
<member name="F:Graphics.Point.x">
<summary>
Instance variable <c>x</c> represents the Point's x-coordinate.
</summary>
</member>
<member name="F:Graphics.Point.y">
565
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
<summary>
Instance variable <c>y</c> represents the Point's y-coordinate.
</summary>
</member>
<member name="M:Graphics.Point.#ctor">
<summary>
This constructor initializes the new Point to (0,0).
</summary>
</member>
<member
name="M:Graphics.Point.#ctor(System.Int32,System.Int32)">
<summary>
This constructor initializes the new Point to
(<paramref name="xor"/>,<paramref name="yor"/>).
</summary>
<param name="xor">The new Point's x-coordinate.</param>
<param name="yor">The new Point's y-coordinate.</param>
</member>
<member name="M:Graphics.Point.Move(System.Int32,System.Int32)">
<summary>
This method changes the point's location to the given
coordinates.
</summary>
<param name="xor">The new x-coordinate.</param>
<param name="yor">The new y-coordinate.</param>
<seealso
cref="M:Graphics.Point.Translate(System.Int32,System.Int32)"/>
</member>
<member
name="M:Graphics.Point.Translate(System.Int32,System.Int32)">
<summary>
This method changes the point's location by the given
x- and y-offsets.
</summary>
<example>
The following code:
<code>
Point p = new Point(3,5);
p.Translate(-1,3);
</code>
566
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
results in <c>p</c>'s having the value (2,8).
</example>
<param name="xor">The relative x-offset.</param>
<param name="yor">The relative y-offset.</param>
<seealso
cref="M:Graphics.Point.Move(System.Int32,System.Int32)"/>
</member>
<member name="M:Graphics.Point.Equals(System.Object)">
<summary>
This method determines whether two Points have the same
location.
</summary>
<param name="o">
The object to be compared to the current object.
</param>
<returns>
True if the Points have the same location; otherwise, false.
</returns>
<seealso
cref="M:Graphics.Point.op̲Equality(Graphics.Point,Graphics.Point)"
/>
<seealso
cref="M:Graphics.Point.op̲Inequality(Graphics.Point,Graphics.Point
)"/>
</member>
<member name="M:Graphics.Point.GetHashCode">
<summary>
Computes the hash code for a Point.
</summary>
<returns>
A hash code computed from the x and y coordinates.
</returns>
</member>
<member name="M:Graphics.Point.ToString">
<summary>
Report a point's location as a string.
</summary>
<returns>
A string representing a point's location, in the form (x,y),
without any leading, training, or embedded whitespace.
567
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
</returns>
</member>
<member
name="M:Graphics.Point.op̲Equality(Graphics.Point,Graphics.Point)"
>
<summary>
This operator determines whether two Points have the same
location.
</summary>
<param name="p1">The first Point to be compared.</param>
<param name="p2">The second Point to be compared.</param>
<returns>
True if the Points have the same location; otherwise, false.
</returns>
<seealso cref="M:Graphics.Point.Equals(System.Object)"/>
<seealso
cref="M:Graphics.Point.op̲Inequality(Graphics.Point,Graphics.Point
)"/>
</member>
<member
name="M:Graphics.Point.op̲Inequality(Graphics.Point,Graphics.Point
)">
<summary>
This operator determines whether two Points have the same
location.
</summary>
<param name="p1">The first Point to be compared.</param>
<param name="p2">The second Point to be compared.</param>
<returns>
True if the Points do not have the same location;
otherwise, false.
</returns>
<seealso cref="M:Graphics.Point.Equals(System.Object)"/>
<seealso
cref="M:Graphics.Point.op̲Equality(Graphics.Point,Graphics.Point)"
/>
</member>
<member name="M:Graphics.Point.Main">
<summary>
<para>
568
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
This is the entry point of the Point class testing program.
</para>
<para>
This program tests each method and operator, and is intended
to be run after any non-trvial maintenance has been performed
on the Point class.
</para>
</summary>
</member>
<member name="P:Graphics.Point.X">
<value>
The Point's x-coordinate.
</value>
</member>
<member name="P:Graphics.Point.Y">
<value>
The Point's y-coordinate.
</value>
</member>
</members>
</doc>
569
X 3015:2008 (ISO/IEC 23270:2006)
2019年7月1日の法改正により名称が変わりました。まえがきを除き,本規格中の「日本工業規格」を「日本産業規格」に読み替えてください。
附属書F
(参考)
参考文献
序文
この附属書は,本体の規定を補足するものであって,規定の一部ではない。
ANSI X3.274:1996,Programming Language-REXX(本書は,浮動小数点10進算術規則を理解する上で
有用となる。)
ISO 31-0:1992,Annex B (informative), Guide to the rounding of numbers(本書では銀行型丸めの定義を与
える。)
JIS X 3010:2003 プログラム言語C
注記 対応国際規格:ISO/IEC 9899:1999,Programming languages−C (IDT)
JIS X 3014:2003 プログラム言語C++
注記 対応国際規格:ISO/IEC 14882:2003,Programming languages−C++ (IDT)