>サイトトップへ >このカテゴリの一覧へ

X 3015:2008 (ISO/IEC 23270:2006)

(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)

ページ

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)

ページ

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)

ページ

11.1.4  単純型

121

11.1.5  整数型

122

11.1.6  浮動小数点型

122

11.1.7  decimal10 進実数)型

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)

ページ

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)

ページ

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  項演算子多重定義解決

164

14.2.5  利用者定義演算子候補

165

14.2.6  数値昇格

165

14.2.6.1  単項数値昇格

166

14.2.6.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)

ページ

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)

ページ

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)

ページ

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)

ページ

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)

ページ

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  virtualsealedoverride 及び 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  項演算子

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)

ページ

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)

ページ

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)

ページ

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)

ページ

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)

まえがき

この規格は,工業標準化法第 14 条によって準用する第 12 条第 1 項の規定に基づき,社団法人情報処理

学会情報規格調査会(ITSCJ)及び財団法人日本規格協会(JSA)から,工業標準原案を具して日本工業規格を

改正すべきとの申出があり,日本工業標準調査会の審議を経て,経済産業大臣が改正した日本工業規格で

ある。

これによって,JIS X 3015:2005 は改正され,この規格に置き換えられた。

この規格は,著作権法で保護対象となっている著作物である。

この規格の一部が,特許権,出願公開後の特許出願,実用新案権又は出願公開後の実用新案登録出願に

抵触する可能性があることに注意を喚起する。経済産業大臣及び日本工業標準調査会は,このような特許

権,出願公開後の特許出願,実用新案権又は出願公開後の実用新案登録出願に係る確認について,責任は

もたない。


日本工業規格

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#を教えようとする教育者。



X 3015:2008 (ISO/IEC 23270:2006)

−  標準 C#について書こうとする著者。

このように,適合性は非常に重要であって,この規格の大部分は,C#実装及び C#プログラムを規格適

合とする特性を規定することを意図している。

注記  この規格において,対応国際規格においては太字で表されている強調部分には下線を施す。

この規格において,それらの要件を規定する本文が規定と考えられる。この規格において,それ以外の

本文は,参考となる。すなわち,付加的情報だけを与える。特に断らない限り,すべての本文は規定とす

る。規定本文は,更に,必す(須)及び条件付きに分かれる。条件付き規定本文は,省略可能な機能及び

その要件を規定する。しかし,その機能が提供される場合には,その構文及び意味は規定されたとおりで

なければならない。

この規格の要件に違反したときの振る舞いは定義しない。この規格において,未定義の動作は,

“未定義

の動作”という用語によってだけ示される。

厳格に適合するプログラムは,この規格で必す(須)とされた機能だけを使う(これは,厳格に適合す

るプログラムが条件付きで規定された機能を一切使えないことを意味する。

。厳格に適合するプログラム

は,いかなる未規定,未定義又は実装定義の動作に依存する出力も生成してはならない。

C#の適合実装は,あらゆる厳格に適合するプログラムを受理しなければならない。

C#の適合実装は,この規格で規定された(条件付き規定を除く。)すべての型,値,オブジェクト,特

性,

メソッド並びにプログラムの構文及びプログラムの意味を提供して使えるようにしなければならない。

C#の適合実装は,JIS X 0221 及び Unicode 標準 4.0 版に適合する文字を解釈しなければならない。適合

実装は,UTF-8 符号化形式で符号化された Unicode ソースファイルを受理しなければならない。

C#の適合実装は,#error 前処理指令を含むソースコードの翻訳に,それが条件付きコンパイルによっ

て読み飛ばされる部分でない限り,成功してはならない。

C#の適合実装は,ソースプログラムが構文規則に違反するか,又は(“しなければならない”,“しては

ならない”

“エラー”又は“警告”と定義された)否定的要件に違反した場合には,その要件に“診断メ

ッセージ出力は不要とする。

”という規定がない限りは,少なくとも一つの診断メッセージを出力しなけれ

ばならない。

C#の適合実装は,厳格に適合したいかなるプログラムの振る舞いをも変えない限り,この規格で示され

ていない追加的な型,値,オブジェクト,特性及びメソッドを提供してもよい。適合実装は,この規格に

違反する拡張機能を使うプログラムを診断できなければならない。ただし,診断の後で,そのようなプロ

グラムをコンパイルし実行してもよい(拡張できるということは,適合実装は,この規格で明示的に予約

した識別子を除き,いずれの識別子も予約していないことを意味する。

C#の適合実装は,すべての実装定義の特徴及びすべての拡張を定義する文書を用意しなければならない。

C#の適合実装は,附属書 に示されたクラスライブラリを提供しなければならない。この規格では,そ

のクラスライブラリを参照する。

適合プログラムとは,適合実装で受理されるプログラムとする(そのようなプログラムには,拡張又は

条件付きで規定された機能が含まれてもよい。

3

引用規格

次に掲げる規格は,この規格に引用されることによって,この規格の規定の一部を構成する。これらの

引用規格のうちで,西暦年を付記してあるものは,記載の年の版を適用し,その後の改正版(追補を含む。

には適用しない。西暦年の付記がない引用規格は,その最新版(追補を含む。

)を適用する。


3

X 3015:2008 (ISO/IEC 23270:2006)

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 00010032 に従って解釈する。この規格で定

義されない数学記号は,JIS Z 8201 に従って解釈する。

4.1

アプリケーション (Application)

入口点をもつアセンブリ(10.1 参照)

。アプリケーション実行に際しては,新しいアプリケーション領域

が生成される。同一のアプリケーションに対する複数の異なるインスタンスが,同一コンピュータ上に同

時に具現化できる。このとき,それぞれのインスタンスは,独自のアプリケーション領域をもつ。

4.2

アプリケーション領域 (Application domain)

アプリケーションの状態のコンテナとして働くことによって,

アプリケーションの隔離を実現するもの。

アプリケーション領域は,アプリケーションで定義された型及びアプリケーションで使用するクラスライ

ブラリのためのコンテナ及び境界として機能する。あるアプリケーション領域にロードされた型は,別の

アプリケーション領域にロードされた同じ型とは区別される。インスタンスを直接共有することはできな

い。例えば,アプリケーション領域ごとに,これらの型の静的変数の複製をもつ。また,型の静的構築子

は,アプリケーション領域ごとに高々1 回しか実行されない。実装は,アプリケーション領域の構築及び

破棄に関する,実装固有の方針又は機構を提供してよい。



X 3015:2008 (ISO/IEC 23270:2006)

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)

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)



X 3015:2008 (ISO/IEC 23270:2006)

プログラム要素の問題があるかもしれない使い方の特定を意図して,プログラムコンパイル中に報告さ

れる参考メッセージ。

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)

例を示す。

≪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

頭字語及び略語

箇条 は,参考であって,規定ではない。

この規格では,次の頭字語及び略語を用いる。

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)。



X 3015:2008 (ISO/IEC 23270:2006)

記述された構成にどのようなものがあるかを説明するために例を与える。関連する箇条を示すために参

照を用いる。実装担当者又はプログラマのための助言又は指導として注記を与える。附属書は,この規格

に含まれる情報のまとめ及び追加情報を与える。

箇条 1∼箇条 5,箇条 の一部,箇条 9∼箇条 26,箇条 27 の冒頭及び

附属書 のほとんどは,この規格

の規定とする。冒頭を除く箇条 27 は,条件付き規定とする。箇条 6,箇条 の一部,箇条 8

附属書 A

BCE及び の一部,注記,例並びに索引は,規定ではない。

全体が規定でないと明示された箇条及び附属書を除けば,規定本文内に含まれる規定でない箇所は,次

のいずれかで示される。

1)  次によって区切られた附属書の一部。

参考  この部分は,規定ではない。

参考  終わり。

2)  例  次の例は,…コード断片,説明が続くことがある。 
3)  注記  説明

参考として記されていないすべての文は,規定とする。

8

言語概要

箇条 は参考であって,規定ではない。

C#(“しーしゃーぷ”と読む。)は,オブジェクト指向及び型安全という特徴を備えた最新の複雑でない

プログラム言語である。C 及び C++のプログラマは,すぐに C#を習得できる。C#は,RAD(迅速なアプ

リケーション開発)言語の生産性と C++の威力とを融合する。

箇条 の残りは,言語の基本機能を示す。箇条 9∼箇条 27 では,規則及び例外について詳細に,場合に

よっては数学的に示す。箇条 では,明確さ及び簡潔さを優先する。手軽なプログラムが書けて,箇条 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)

−  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)

}

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)

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)

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)

−  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)

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)

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)

この宣言は,次のように省略できる。プログラム上の意味は違わない。

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)

 }

}

この例は,ボックス化及びボックス化解除の両方を示す。値型の変数を参照型に変換する必要がある場

合は,値を保持するためのオブジェクトの箱が割り当てられて,その箱に値がコピーされる。ボックス化

解除では,この逆の処理が行われる。オブジェクトの箱が元の値型にキャストされるときは,箱の値がコ

ピーされて,適切な記憶域に格納される。

この型システム統合によって,不必要な負荷を発生することなく,オブジェクトとしての利点が値型に

付加される。オブジェクトのような振る舞いを 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)

}

この例では,値が代入される前に,その変数 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)

参照仮引数は,

“参照による”仮引数渡しのために用いられる。これは,仮引数が,呼出し側が提供した

実引数の別名として振る舞う。参照仮引数自体は,変数を定義せずに,対応する実引数の変数を参照する。

参照仮引数を変更すると,対応する実引数が影響を受ける。参照仮引数は,修飾子 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)

   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)

args[0] = 1

args[1] = 2

args[2] = 3

# of arguments: 4

args[0] = 1

args[1] = 2

args[2] = 3

args[3] = 4

箇条 の例の多くは,クラス 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)

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)

}

これは,クラス 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)

インスタンスを固定し,ポインタ操作を使って要素に対する繰返し処理を行う。各配列要素の添字,値及

び位置が,端末に出力される。次に,出力の例を示す。

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)

次の表は,使用できる文の種類及びそれぞれについての例を示す。

文並び及びブロック文

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)

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)

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)

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)

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)

の例は,二つの公開定数をもつ 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)

及び静的構築子の中だけで行える。静的読込み専用フィールドは,静的構築子の中で代入可能であり,非

静的読込み専用フィールドは,インスタンス構築子の中で代入可能である。したがって,クラス 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)

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)

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)

 …

}

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)

// 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)

 }

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)

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)

}

 }

 …

}

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)

}

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)

{

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)

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)

 }

}

非抽象クラス 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)

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)

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)

{

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)

委譲インスタンスの興味深く有用な性質としては,委譲インスタンスがカプセル化しているメソッドの

クラスについては,何も知らず,関心もないということがある。重要なのは,これらのメソッドと委譲の

型との間に一貫性がある(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)

 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)

クラスライブラリは,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)

できる。別名は,複数のクラスライブラリの間で名前の衝突が発生する状況で,又は大きい名前空間に含

まれる少数の型を使用する場合に,

役に立つ。

次に,

クラス 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)

}

ここまではよかったが,次に版管理問題が発生する。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)

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)

使われていないならば,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)

{

 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)

{

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)

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)

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)

を 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)

を使って総称型宣言の内部にある文を実行しようとすることも多い。

例えば,上の 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)

{

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)

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)

ドを展開して書かれることになり,メソッドのソースコードと委譲インスタンスとが便利に結び付けられ

る。この利便性に加えて,無名メソッドは,それを含む関数メンバの局所状態へのアクセスを共有できる。

名前付きメソッドを用いて,この状態共有を実現すると,局所変数をオブジェクトのフィールドに“もち

上げる”必要があるので,ソースコードが更に分かりにくくなる。

無名メソッドは,次のように,≪無名メソッド式≫を用いて定義される。

≪無名メソッド式≫:

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)

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)

}

 );

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)

列挙子を返す仮引数のないメソッド 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)

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)

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)

インタフェースを返すメソッド 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)

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)

次の例では,二つの 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)

これは,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.39.4 及び 9.5 で示す。字句文法の終端記号は,Unicode 文字集合の文字とし,字句

文法は,どのように文字を組み合わせて字句(9.4 参照)

,空白類(9.3.3 参照)

,注釈(9.3.2 参照)及び前

処理指令(9.5 参照)を形成するかを規定する。

C#プログラムのソースファイルは,すべて,字句文法の生成規則≪入力≫(9.3 参照)に適合しなけれ

ばならない。

9.2.2

構文文法

C#の構文文法については,箇条 9∼箇条 27 及び附属書 で規定する。構文文法の終端記号は,字句文

法で定義される字句とする。構文文法では,字句を組み合わせて C#プログラムを構成する方法を指定する。

C#プログラムのソースファイルは,すべて,構文文法の生成規則≪コンパイル単位≫(16.1 参照)に適

合しなければならない。

9.2.3

文法のあいまい性

≪単純名≫(14.5.2 参照)及び≪メンバアクセス≫(14.5.4 参照)のための生成規則は,式のための文法


71

X 3015:2008 (ISO/IEC 23270:2006)

上のあいまい性を生じさせる可能性がある。

例  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)

≪入力要素群≫::

≪入力要素≫

≪入力要素群≫  ≪入力要素≫

≪入力要素≫::

≪空白類≫

≪注釈≫

≪字句≫

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)

占める。

/* 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)

≪区切り注釈≫::

/*

≪区切り注釈テキスト≫

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)

≪文字列リテラル≫

≪演算子又は区切り子≫

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)

る規則に厳密に対応する。

≪識別子≫::

≪利用可能識別子≫

@

≪識別子又はキーワード≫

≪利用可能識別子≫::

≪キーワード≫でない≪識別子又はキーワード≫

≪識別子又はキーワード≫::

≪識別子開始文字≫≪識別子部分文字群≫

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)

の識別子を使用しなければならない。正規形 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)

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.217.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)

≪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 (2

31

)の≪10 進整数リテラル≫が,単項負演算子字句

14.6.2 参照)

の直後に続く字句であるときに,

(二つの字句の)

結果は,

型が int で値が−2147483648

(−2

31

)の定数となる。それ以外の状況では,このような≪10 進整数リテラル≫は uint 型になる。

−  ≪整数型接尾辞≫をもたないか≪整数型接尾辞≫が  L  又は l で,

値が 9223372036854775808 (2

63

)の≪

10 進整数リテラル≫が単項負演算子字句(14.6.2 参照)の直後に続く字句であるときに,(二つの字句

の)結果は,型が long で値が−9223372036854775808 (−2

63

)の定数となる。それ以外の状況では,こ

のような≪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)

≪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)

規則 に準じる。

値が丸められない限り,リテラルの小数部けた数は保持される。

注記  したがって,リテラル 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)

注記  ≪文字≫において,逆斜線文字(¥)の直後の文字は,次のいずれかでなければならない。',

"

,¥,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)

≪単一通常文字列リテラル文字≫

≪単純逆斜線表記≫

≪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)

てそのまま逐語的に保持され,二重引用文字の対は,それぞれ一つの二重引用文字に置き換えら

れる。

注記 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)

≪右シフト代入≫::

> >= 

≪右シフト≫は,二つの字句>及び>から構成される。同様に,≪右シフト代入≫は,二つの字句>及び

>=

から構成される。これらの生成規則群においては,二つの字句の間には,構文文法における他の生成規

則とは異なり,

(空白類を含めた)任意の種類の文字を置くことは許されない。

注記 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)

#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)

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)

ソースファイルでの指令#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)

る。

≪前処理条件≫::

≪前処理 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)

≪条件節≫が選択されると,その部分は通常の≪入力節≫として処理される。すなわち,その部分に含

まれるソースコードは,字句文法に準拠している必要があり,その部分のソースコードから字句が生成さ

れて,その部分内の前処理指令は規定どおりに機能する。

≪条件節≫が残っていた場合,その部分は≪読み飛ばし節≫として処理される。すなわち,前処理指令

を除き,セクション内のソースコードは字句文法に準拠している必要はなく,その部分のソースコードか

らは,字句が生成されず,その部分内の前処理指令は,字句的に正しい必要があるが,それ以外の処理は

されない。≪読み飛ばし節≫として処理される≪条件節≫の内部では,(入れ子になった構成要素

#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)

ている必要があることに注意する。

複数行入力要素の内部にある前処理指令は,処理されない。次に示すプログラムは,その例で

ある。

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)

≪改行≫

≪空白類≫   ≪入力文字群≫

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)

≪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)

注記  ≪前処理プラグマテキスト≫は,任意のテキストを保持できる。具体的には,整形式の字句を

含む必要がない。

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)

ス可能性にも,取り囲む型宣言の宣言されたアクセス可能性にもかかわりなく,実行環境はアプリケーシ

ョンの入口点にアクセスできる。

入口点のメソッドは,総称クラス宣言(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)

−  プログラムのすべてのソースファイルにおいて,取り囲む≪名前空間宣言≫がない≪名前空間メンバ

宣言≫  は,大域的宣言空間と呼ばれる単一の組み合わされた宣言空間のメンバとする。

−  プログラムのすべてのソースファイルにおいて,同一の完全限定名前空間名をもつ≪名前空間宣言≫

内の≪名前空間メンバ宣言≫は,単一の組み合わされた宣言空間のメンバとする。

−  各≪コンパイル単位≫及び≪名前空間本体≫は,別名宣言空間をもつ。≪コンパイル単位≫又は≪名

前空間本体≫の各≪外部別名指令≫及び≪using 別名指令≫は,別名宣言空間へメンバを寄与する

16.4.1 参照)

−  部分型でないクラス宣言,構造体宣言又はインタフェース宣言は,新しい宣言空間を作る。部分クラ

ス,部分構造体又は部分インタフェース宣言は,すべての同じプログラム中で合致する部分によって

共有される宣言空間へ寄与する。名前は,≪型仮引数並び≫,≪クラスメンバ宣言≫,≪構造体メン

バ宣言≫又は≪インタフェースメンバ宣言≫を通して,この宣言空間に導入される。多重定義された

インスタンス構築子宣言及び多重定義された静的構築子宣言を除き,クラスメンバ又は構造体メンバ

の宣言は,クラス又は構造体と同じ名前でメンバを導入することはできない。クラス,構造体又はイ

ンタフェースでは,メソッド及び添字子を多重定義宣言できる。さらに,クラス又は構造体では,イ

ンスタンス構築子,演算子及び型を多重定義宣言できる。

例  クラス,構造体又はインタフェースは,それぞれのメソッド宣言が呼出し情報で異なるなら

ば,同一名の複数のメソッド宣言を含むことができる(10.6 参照)

。クラス又は構造体は,型

仮引数の数が異なる型によって与えられる名前と同じ名前をもつ複数の入れ子型を含むこと

ができる。

基底クラスは,クラスの宣言空間に寄与しないし,基底インタフェースは,インタフェースの宣言

空間に寄与しない。したがって,派生クラス又は派生インタフェースでは,継承されたメンバと同じ

名前でメンバを宣言できる。

−  各列挙型宣言では,新しい宣言空間が作成される。名前は,≪列挙メンバ宣言≫を通してこの宣言空

間に導入される。

−  各≪ブロック≫,≪switch ブロック≫,≪for 文≫,≪foreach 文≫又は≪using 文≫は,局所

変数宣言空間と呼ばれる局所変数及び局所定数のための宣言空間を作成する。名前は,≪局所変数宣

言≫及び≪局所定数宣言≫を通じて,この宣言空間に導入される。ブロックがインスタンス構築子,

メソッド若しくは演算子の宣言の本体である場合,又は添字子宣言の get アクセス子若しくは set

アクセス子である場合,それらの宣言内で宣言される仮引数は,ブロックの局所変数宣言空間のメン

バとする。ブロックが総称メソッドの本体の場合,その総称メソッド宣言で宣言された型仮引数は,

そのブロックの局所変数宣言空間のメンバとする。局所変数宣言空間の二つのメンバが同じ名前をも

つことはエラーとする。局所変数宣言空間及び入れ子になった局所変数宣言空間が同じ名前の要素を

含むことはエラーとする。

注記  このように入れ子になったブロックの中では,外側の局所変数又は局所定数と同じ名前をもっ

た,局所変数又は局所定数を宣言することはできない。二つの入れ子になったブロックにおい

て,一方が他方を含んでいない限り,その二つのブロックで同じ名前をもった要素を含むこと

はできる。

−  各≪ブロック≫又は≪switch ブロック≫は,ブロックのラベル宣言空間と呼ばれるラベルのための

宣言空間を別途作成する。それらの名前は,≪ラベル付き文≫を通じて,この宣言空間に導入され,

goto

文を通じて参照される。ブロックのラベル宣言空間及び入れ子になったブロックのラベル宣言


96 
X 3015:2008 (ISO/IEC 23270:2006)

空間が同じ名前の要素を含むことはエラーとする。

したがって,入れ子になっているブロックの中で,

外側のブロックのラベルと同じ名前のラベルを宣言することはできない。

注記  二つの入れ子になったブロックにおいて,一方が他方を含んでいない限り,その二つのブロッ

クで同じ名前をもった要素を含むことができる。

名前を宣言する際に,記述の順序は一般的に重要ではない。特に,名前空間,定数,メソッド,特性,

イベント,添字子,演算子,インスタンス構築子,終了化子,静的構築子及び型を宣言したり使用したり

する場合,記述の順序は重要ではない。宣言の順序は,次の場合に重要となる。

−  フィールド宣言及び局所変数の場合は,宣言の順序によって,初期化子(存在する場合)の実行順序

が決まる。同じ型のための複数の部分型宣言のフィールド宣言がある場合,その部分の順序は不定と

する。しかし,それぞれのフィールド初期化子部分の中は順番に実行される。

−  局所変数及び局所定数は,それらが使われる前に定義されなければならない(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)

{

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)

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)

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)

注記  クラスのメンバとして宣言された型は,任意の 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)

た任意の型のプログラムテキストとの和集合とする。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)

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)

}

このコードでは,クラス 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)

ス型とする場合に許可される。

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)

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)

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)

まれる派生クラスの≪クラス本体≫まで広げられる。

−  ≪構造体メンバ宣言≫(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)

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)

 }

}

名前 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)

  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)

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)

次で説明する解決規則に従って型を参照する必要がある。この規則に従わないと,コンパイル時エラーに

なる。

≪限定別名メンバ≫の構文及び意味は,16.7 で定義される。

≪限定別名メンバ≫でない≪名前空間名又は型名≫は,次の四つの形式のどれか一つをもつ。

−  I

−  I<A

1

,

 ..., A

K

>

−  N.I

−  N.I<A

1

,

 ..., A

K

>

ここで,I は,単一の識別子で,N は≪名前空間名又は型名≫,<A

1

,

 ..., A

K

>

は選択的な≪型実引数並び

≫とする。≪型実引数並び≫が指定されていない場合,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)

なる。

・  そうでない場合,≪名前空間名又は型名≫は与えられた型実引数で構築された型を参照する。

−  そうでない場合で,≪名前空間名又は型名≫がある場所は,N の名前空間宣言によって囲まれて

いる場合

・  K がゼロで移入された名前空間又は型が I という名前で関連付けられている≪外部別名指令≫

又は≪using 別名指令≫を包含する場合,≪名前空間名又は型名≫は,その名前空間又は型を

参照する。

・  そうでない場合で,名前空間宣言の≪using 名前空間指令≫によって移入された名前空間が,

I

という名前及び型仮引数 K をもつ唯一の型を包含する場合,≪名前空間名又は型名≫は,与

えられた型実引数で構築された型を参照する。

・  そうでない場合で,名前空間宣言の≪using 名前空間指令≫によって移入された名前空間が,

一つ以上の I という名前及び型仮引数 K をもつ型を包含する場合,

≪名前空間名又は型名≫は,

あいまいでエラーになる。

・  そうでない場合,≪名前空間名又は型名≫は未定義で,コンパイルエラーになる。

−  そうでない場合で,≪名前空間名又は型名≫が N.I の形式又は N.I<A

1

, ..., A

K

>

の形式の場合,N

は,≪名前空間名又は型名≫としてまず解決される。N の解決が成功しなかった場合,コンパイル時

エラーになる。さもなければ,≪名前空間名又は型名≫が N.I の形式又は N.I<A

1

, ..., A

K

>

の形

式は,次のように解決される。

・  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)

−  宣言がコンパイル単位に直接含んでおらず,他の宣言のいずれにも入れ子になっていない場合,その

完全限定名は 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)

リ管理の全段階は次となる。

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)

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)

{

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)

ルドに対しては動作を一切行わないようにすることを推奨する。

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)

型の集合を提供する。単純型は,予約語によって識別する。

≪値型≫:

≪構造体型≫

≪列挙型≫

≪構造体型≫:

≪型名≫

≪単純型≫

≪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)

値型の変数への代入は,代入される値の複製を生成する。これは,参照型の変数への代入と異なる点で

あり,参照型変数の場合,参照を複製するが,参照によって示されるオブジェクトを複製しない。

注記  すべての値型は,クラス 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)

引数なしの構築子の明示的な宣言を含めてはならない。ただし,仮引数付きのインスタンス構築子を構造

体型で宣言してもよい(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)

−  単純型にかかわる変換が,他の構造体型で定義された変換演算子の評価で使用されることはあるが,

利用者定義の変換演算子を別の利用者定義演算子の評価(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)

例  例えば,1.0 / 0.0 は正の無限大になり,–1.0 / 0.0 は負の無限大になる。

−  非数値。しばしば NaN と省略表記される。NaN は,ゼロをゼロで除すなどの不当な浮動小数点演算が

行われた場合に発生する。

−  s × m × 2

e

の形式で表される有限のゼロ以外の値。s は 1 又は-1 で,m 及び e は浮動小数点型の種

類によって決まる。float の場合は,0 < m < 2

24

及び-149  ≦  e  ≦  104 で,double の場合は,

0 < m < 2

53

及び-1075  ≦  e  ≦  970 とする。正規化されない浮動小数点数は,有効なゼロ以外の

値とみなす。C#は,その適合実装の正規化されない浮動小数点数については触れず,禁止も奨励もし

ない。

float

型は,7 けた(桁)の有効けた数で,約 1.5 × 10

-45

∼3.4 × 10

38

の範囲の値を表現すること

が可能とする。

double

型は,15∼16 けた(桁)の有効けた数で,約 5.0 × 10

-324

∼ 1.7 × 10

308

の範囲の値を表

現することが可能とする。

代入演算子を含む浮動小数点演算子では,例外は発生しない。浮動小数点演算で例外状況が発生した場

合は,次の規則に従って,ゼロ,無限大又は NaN を生成する。

−  浮動小数点演算の結果は,結果格納先の書式において,表現可能な直近の値に丸める。この結果,ゼ

ロ以外の値がゼロに丸められる場合がある。

−  浮動小数点演算の結果,値の絶対値が結果格納先の書式に対して大き過ぎる場合は,演算の結果は正

の無限大又は負の無限大とする。

−  浮動小数点演算が無効である場合は,演算の結果は NaN とする。

−  浮動小数点演算の演算対象の一方又は両方が NaN である場合は,演算の結果は NaN とする。

浮動小数点演算は,結果の型より高い精度で実行してもよい。

例  例えば,double 型よりも値域が広くより正確な“extended”又は“long double”浮動小数

点型を支援するハードウェアアーキテクチャでは,浮動小数点演算を暗黙によって精度の高い型

を使って実行する場合がある。このようなハードウェアアーキテクチャが,低精度の浮動小数点

数を扱うときにも高負荷の演算を行う設計になっている場合,性能及び精度の両方を低下させる

ような実装を強制することは望ましくないため,C#ではどのような浮動小数点演算にも高精度デ

ータ型を使用することを容認している。より正確な結果が必要な場合を除き,これによって大き

な違いが出ることはあまりない。ただし,x * y / z の形式の式で,乗算の結果が double の

値域を超え,除算によって再び結果が double の範囲に戻る場合には,より大きい範囲で式が評

価されるため,無限大ではなく有限値が生成される場合がある。

11.1.7  decimal10 進実数)型

decimal

型は,財務計算や通貨計算に適した 128 ビットデータ型とする。decimal 型は,少なくとも

28 けた(桁)以上の有効けた数で,1×10

28

から 1×10

28

までの範囲の値を表現することが可能とする。

decimal

型の有限の値の集合は,(−1)

s

×c×10

e

の形式とする。は 0 又は 1,係数 は 0≦cCmax

小数部けた数 は EmineEmaxCmax は少なくとも 1×10

28

以上,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)

数値は無限の 2 進展開になることが多く,丸め誤差が発生する可能性が高くなる。

decimal

型の値どうしの演算結果は,正確な結果(各演算子に対して定義された小数部けた数を保持し

た値)を計算してから,表現に合わせて丸められた値とする。結果は,最も近い表現できる値に丸める。

表現できる二つの値との差が等しい場合は,JIS Z 8401 の 2 c) 

規則 に従って最下位のけた(桁)位置が

偶数の値に丸められる。この方式は,

“偶数丸め方式”と呼ばれる。結果は,少なくとも 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)

≪型名≫

≪配列型≫:

≪非配列型≫   ≪位階指定子群≫

≪非配列型≫:

≪値型≫

≪クラス型≫

≪インタフェース型≫

≪委譲型≫

≪型仮引数≫

≪位階指定子群≫:

≪位階指定子≫

≪位階指定子群≫   ≪位階指定子≫

≪位階指定子≫:

 [

≪次元区切り子群≫

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)

インタフェース型については,箇条 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)

に振る舞う。

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)

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)

値型(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.112.5 で規定するとおり,変数の状態は,初期代入あり又は初期代入なしのいずれかとする。初期

代入ありの変数は,適切に定義された初期値をもち,常に確実に代入されているものとみなされる。初期

代入なしの変数は,初期値をもたない。初期代入なしの変数が特定の位置において確実に代入されている

ものとみなされるには,その位置に至る可能性のあるすべての実行経路において,その変数への代入が行

われていなければならない。


130 
X 3015:2008 (ISO/IEC 23270:2006)

12.1  変数の種類

C#で定義されている変数の種類は,静的変数,インスタンス変数,配列要素,値仮引数,参照仮引数,

出力仮引数及び局所変数の 7 種類とする。12.1.112.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)

確実な代入の検査に当たっては,配列要素は初期代入ありとみなされる。

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)

≪局所変数宣言≫は,≪ブロック≫,≪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)

数が自動的に初期化されていること,又は少なくとも 1 回の代入の対象になっていることが証明された場

合,その変数はその位置において確実に代入されていると呼ばれる。

注記  簡単にいえば,確実な代入の規則は,次のとおりとする。

−  初期代入ありの変数(12.3.1 参照)は,常に確実に代入されているとみなされる。

−  初期代入なしの変数(12.3.2 参照)は,ある位置に到達するまでに通過する可能性のあるすべての実

行経路に次に示す少なくとも一つが含まれている場合,その位置においては確実に代入されているも

のとみなされる。

・  変数が左演算対象として指定されている単純な代入(14.14.1 参照)

・  変数を出力仮引数として渡す呼出し式(14.5.5 参照)又はオブジェクト生成式(14.5.10.1 参照)

・  局所変数の場合は,変数初期化子を含む局所変数宣言(15.5 参照)

上記の規則の基となる正式な規定については,12.3.112.3.2 及び 12.3.3 による。

≪構造体型≫変数のインスタンス変数に関する確実に代入された状態は,全体としてだけではなく個別

にも追跡される。≪構造体型≫変数及びそのインスタンス変数には,上記の規則に加えて次の規則も適用

される。

−  ≪構造体型≫変数が確実に代入されているとみなされる場合は,そこに含まれるインスタンス変数も

確実に代入されているとみなされる。

−  ≪構造体型≫変数のすべてのインスタンス変数が確実に代入されているとみなされる場合は,その構

造体型の変数も確実に代入されているとみなされる。

確実な代入は,次の文脈における要件とする。

−  変数の値を取得するすべての位置において,その変数は,確実に代入されていなければならない。

注記  これによって,未定義の値が発生しないことが保証される。

−  式の中で使われている変数については,次の場合を除き,変数の値を取得するとみなす。

・  変数が,単純な代入の左演算対象になっている場合。

・  変数が,出力仮引数として渡されている場合。

・  変数が≪構造体型≫変数で,メンバアクセスの左演算対象として指定されている場合。

−  変数は,参照仮引数として渡されるすべての位置において,確実に代入されていなければならない。

注記  これによって,呼び出される関数メンバは,参照仮引数を初期代入ありとみなしてもよい

ことが保証される。

−  関数メンバのすべての出力仮引数は,関数メンバが(return 文によって,又は実行が関数メンバ本

体の終端に達したことで)

制御を返すすべての位置において,

確実に代入されていなければならない。

注記  これによって,関数メンバが出力仮引数で未定義の値を返さないことが保証されるので,

コンパイラは,出力仮引数として変数を受け取る関数メンバ呼出しが変数への代入と同じ

であるとみなしてもよい。

−  ≪構造体型≫インスタンス構築子の this 変数は,インスタンス構築子が制御を返すすべての位置で

確実に代入されていなければならない。

12.3.1  初期代入ありの変数

次の種類の変数は,初期代入ありに分類される。

−  静的変数

−  クラスインスタンスのインスタンス変数

−  初期代入ありの構造体変数のインスタンス変数


134 
X 3015:2008 (ISO/IEC 23270:2006)

−  配列要素

−  値仮引数

−  参照仮引数

−  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  文についての一般的な規則

−  関数メンバ本体の先頭では,は確実には代入されていない。

−  到達不能な文の先頭では,は確実に代入されている。

−  その他すべての文の先頭での確実な代入についての の状態は,その文の先頭に制御を移すすべての

制御フロー遷移での確実な代入についての の状態を検査して決定される。このようなすべての制御

フロー遷移で が確実に代入されている場合であって,その場合に限り,文の先頭で は確実に代入

されている。遷移可能な制御フローの集合の決定には,文の到達可能性の検査(15.1 参照)の場合と

同じ方法を使用する。

−  ブロック文の終了点,及び checked,unchecked,if,while,do,for,foreach,lock,using,


135

X 3015:2008 (ISO/IEC 23270:2006)

switch

の各文の終了点での確実な代入についての の状態は,その文の終了点に制御を移すすべて

の制御フロー遷移での確実な代入についての の状態を検査して判断される。このようなすべての制

御フロー遷移で に確実に代入されている場合は,文の終了点で は確実に代入されている。それ以

外の場合,文の終了点で は確実には代入されていない。遷移可能な制御フローの集合の決定には,

文の到達可能性の検査(15.1 参照)の場合と同じ方法を使用する。

12.3.3.2  ブロック文,checked 文及び unchecked 

ブロック内の文並びの最初の文(文並びが空の場合は,ブロックの終了点)への制御遷移での確実な代

入についての の状態は,ブロック文,checked 文又は unchecked 文より前の確実な代入についての v

の状態と同じとする。

12.3.3.3  式文

式 expr で構成される式文 stmt の場合は,次の規則に従う。

−  式 expr の先頭では,確実な代入についての の状態は stmt の先頭と同じとする。

−  式 expr の末尾で に確実に代入されている場合,stmt の終了点では確実に代入されている。それ

以外の場合,stmt の終了点では確実には代入されていない。

12.3.3.4  宣言文

−  stmt を初期化子のない宣言文とするならば,stmt の終了点では,確実な代入についての の状態は

stmt

の先頭と同じとする。

−  stmt を初期化子をもつ宣言文とするならば,確実な代入についての の状態を決定するときには,

stmt

をあたかも文並びであるかのようにみなす。文並びを,初期化子をもつ宣言ごとに一つの代入

文が(宣言の順序に従って)あるとして処理する。

12.3.3.5  if 

次の形式の if 文 stmt を考える。

if (expr)then-stmt  else  else-stmt

この stmt は,次の規則に従う。

−  expr の先頭では,確実な代入についての の状態は stmt の先頭と同じとする。

−  expr の末尾で が確実に代入されている場合は,then-stmt 又は else-stmt(else 節がないと

きは stmt の終了点)への制御フロー遷移で は確実に代入されている。

−  expr の末尾の が“真式の後では確実に代入される”状態の場合,then-stmt への制御フロー遷移

では確実に代入されるが,else-stmt への制御フロー遷移,及び else 節がないときは stmt の終了

点への制御フロー遷移では確実には代入されない。

−  expr の末尾の が“偽式の後では確実に代入される”状態の場合,else-stmt への制御フロー遷移

では確実に代入されるが,then-stmt への制御フロー遷移では確実には代入されない。また,stmt

の終了点で確実に代入されているとみなされるのは,then-stmt の終了点で確実に代入されている

場合だけとする。

−  上記以外の場合は,then-stmt への制御フロー遷移,又は else-stmt(又は,else 節がないとき

の stmt の終了点)への制御フロー遷移のいずれにおいても,は確実には代入されていないとみな

す。

12.3.3.6  switch 

制御式 expr をもつ switch 文 stmt は,次の規則に従う。

−  expr の先頭での確実な代入についての の状態は,stmt の先頭の の状態と同じとする。


136 
X 3015:2008 (ISO/IEC 23270:2006)

−  到達可能な switch ブロックの文並びへの制御フロー遷移での確実な代入についての の状態は,

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)

式中で≪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≫(は任意)の先頭での確実な代入についての expr の状態は,stmt の先頭で

の確実な代入についての expr の状態と同じとする。

−  ≪try ブロック≫及びすべての≪catch ブロック i≫(1∼n のすべての i)の終了点で expr が確実

に代入されている場合であって,かつ,その場合に限り,stmt の終了点で expr は確実に代入され

ている。


138 
X 3015:2008 (ISO/IEC 23270:2006)

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)

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 の先頭での確実な代入についての の状態は,stmt の先頭の の状態と同じとする。

−  ≪埋込み文≫の終了点,すなわち stmt の終了点への制御フロー遷移での確実な代入についての 

状態

は,expr の末尾での の状態と同じとする。

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)

−  ≪埋込み文≫への制御フロー遷移での確実な代入についての 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.714.814.9 及び 14.10

参照)

,複合代入式(14.14.2 参照)

,checked 式及び unchecked 式(14.5.12 参照)

,配列生成式及び委譲

生成式(14.5.10 参照)には,次の規則が適用される。

各式には,決められた順序で無条件に評価される一つ以上の部分式がある。

例  例えば,2 項演算子%は,演算子の左辺を評価し,次に右辺を評価する。添字指定演算では,まず

添字指定された式を評価してから,左から右へ順に添字式を一つずつ評価する。

この順序で評価される部分式 expr1expr2,... exprn をもつ式 expr は,次の規則に従う。

−  expr1 の先頭での確実な代入についての expr の状態は,expr の先頭での確実に代入された状態と

同じとする。

−  expriは 1 より大きい)の先頭での確実な代入についての expr の状態は,expri1 の末尾での

確実に代入された状態と同じとする。

−  expr の末尾での確実な代入についての expr の状態は,exprn の末尾での確実に代入された状態と

同じとする。

12.3.3.21  呼出し式及びオブジェクト生成式

呼出し式 expr は,次の形式をとるとする。

primary-expression    (arg

1

arg

2

, …, arg

n

)

オブジェクト生成式,expr は,次の形式をとるとする。

new  type  (arg

1

arg

2

, …, arg

n

)

−  呼出し式の場合,primary-expression の前の確実な代入についての expr の状態は,expr の前

の expr の状態と同じとする。

−  呼出し式の場合,arg

1

の前の確実な代入についての expr の状態は,primary-expression の後の

expr

の状態と同じとする。

−  オブジェクト生成式の場合,arg

1

の前の確実な代入についての expr の状態は,expr の前の expr

の状態と同じとする。

−  実引数 arg

i

については,arg

i

の後の確実な代入についての expr の状態は,通常の式の規則によっ

て判断され,ref 修飾子や out 修飾子は無視される。

−  が 1 より大きい実引数 arg

i

については,

arg

i

より前の確実な代入についての expr の状態は,

argi

1

の後の expr の状態と同じとする。

−  任意の実引数で,変数 expr を out 実引数(

“out expr”の形式の実引数)として渡した場合,expr

の後の expr は,代入が確実に行われている。それ以外の場合,expr の後の expr の状態は,arg

n


141

X 3015:2008 (ISO/IEC 23270:2006)

の後の expr の状態と同じとする。

12.3.3.22  単純な代入式

w = expr-rhs

という形式の式 expr は,次の規則に従う。

−  より前の確実な代入についての の状態は,expr より前の確実な代入についての の状態と同じ

とする。

−  expr-rhs より前の確実な代入についての の状態は,より後の確実な代入についての の状態と

同じとする。

−  変数 と が同じならば,expr の後の は,確実に代入されている。それ以外の場合,expr の後

の確実な代入についての の状態は,

expr-rhs

の後の確実な代入についての の状態と同じとする。

例  次のコードについて考える。

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)

{

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 は確実には代入されていない。

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)

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)

する。

・  それ以外の場合で,expr-true の後の expr が確実に代入されており,更に,expr-false の後

の expr が確実に代入されているならば,expr の後の expr は確実に代入されている。

・  それ以外の場合は,expr の後の expr は確実には代入されていない。

12.3.3.27  無名メソッド式

無名メソッド(14.5.15 参照)の仮引数の確実な代入についての状態は,名前付きメソッドの仮引数の場

合と同じとする。つまり,参照仮引数及び値仮引数は確実に初期代入があり,出力仮引数は初期代入がな

い。さらに,出力仮引数は,無名メソッドから正常に返る前に,確実に代入されなければならない(12.1.6

参照)

≪無名メソッド式≫の≪ブロック≫への制御遷移における確実な代入についての外変数 の状態は,≪

無名メソッド式≫の前の確実な代入についての の状態と同じとする。つまり,外変数の確実な代入は,

≪無名メソッド式≫の文脈から継承する。≪無名メソッド式≫の≪ブロック≫内では,確実な代入は,通

常のブロック内と同じように展開する(12.3.3 参照)

≪無名メソッド式≫の後の確実な代入についての変数 の状態は,その≪無名メソッド式≫の前の確実

な代入についての状態と同じとする。

例  次に例を示す。

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)

−  expr の先頭では,確実な代入についての変数 の状態は stmt の先頭と同じとする。

−  式 expr の末尾で変数 に確実に代入されている場合,stmt の終了点では確実に代入されている。

それ以外の場合,stmt の終了点では確実には代入されていない。

12.3.3.29  ??

式 expr は,次の形式をとるとする。

expr-first  ??  expr-second

−  expr-first の前の確実な代入についての の状態は,expr の前の確実な代入についての の状態

と同じとする。

−  expr-second より前の確実な代入についての の状態は,expr-first より後の確実な代入につい

ての状態と同じとする。

−  expr より後の確実な代入についての の状態は,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)

−  暗黙の数値変換

−  暗黙の列挙変換

−  暗黙の参照変換

−  ボックス化変換

−  暗黙の型仮引数変換

−  暗黙の定数式変換

−  利用者定義の暗黙の変換

−  無名メソッド式から互換性のある委譲型への暗黙の変換

−  メソッドグループから互換性のある委譲型への暗黙の変換

−  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)

13.1.4  暗黙の参照変換

暗黙の参照変換には次のものがある。

−  任意の≪参照型≫から object への変換。

−  任意の≪クラス型≫ S から任意の≪クラス型≫ T への変換。ただし,S が T から派生している場合。

−  任意の≪クラス型≫ S から任意の≪インタフェース型≫ T への変換。ただし,S が T を実装している

場合。

−  任意の≪インタフェース型≫ S から≪インタフェース型≫ T への変換。ただし,S が T から派生して

いる場合。

−  要素型が S

E

の任意の≪配列型≫ S から要素型が T

E

の≪配列型≫ T への変換。ただし,次の条件がす

べて満たされる場合。

・  S 及び T は要素型だけが異なる。すなわち,S 及び T の次元数が同じである。

・  S

E

から T

E

への暗黙の参照変換が存在する。

−  一次元の≪配列型≫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)

さ れ る 任 意 の ≪ イ ン タ フ ェ ー ス 型 ≫ に 暗 黙 に 変 換 す る こ と を 許 す 。 ま た , 同 様 に 任 意 の 列 挙 型 は

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)

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)

−  ある整数型から別の整数型への変換について,

処理は変換が行われるけた

(桁)

あふれ検査文脈

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)

又は 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 から派

生しない場合とする。

−  要素型が S

E

の≪配列型≫ S から要素型が T

E

の≪配列型≫ T への変換。ただし,次のすべての条件を

満たす場合とする。

・  S 及び T は,要素型だけが異なる(すなわち,S と T が同じ次元数をもつ。

・  S

E

から T

E

への明示的な参照変換が存在する。

−  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)

明示的な参照変換は≪参照型≫間の変換なので,正しいことを確認するために実行時検査が必要となる。

実行時に明示的な参照変換が成功するには,変換元演算対象の値は 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)

−  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)

検出することを中心に行われる。この決定作業は,次の段階に分解される。

−  利用者定義変換演算子を考慮する基になるクラス及び構造体の集合を探す。この集合は,変換元の型

及びその基底クラス並びに変換先の型及びその基底クラスから成る。ここでは,クラス及び構造体だ

けが利用者定義型演算子を宣言でき,クラス以外の型には基底クラスがないことを暗黙に仮定する。

末尾に修飾子?が付いている場合,利用者定義変換演算子を考慮する基になる型の集合を決定する前に,

変換元及び変換先の型から?を取り除く。例えば,型 S?から型 T?への変換の場合,利用者定義変換演

算子の基になる型の集合は,S と T から成る。

−  発見された型の集合から,適用可能な利用者定義変換演算子を決定する。変換演算子が適用可能であ

るためには,変換元の型から演算子の演算対象の型への標準変換(13.3 参照)を実行でき,演算子の

結果の型から変換先の型への標準変換を実行できなければならない。変換元の型及び変換先の型の両

方が null 許容の場合,適用可能な変換演算子は,利用者定義変換演算子だけではなく,もち上げら

れた変換演算子(13.7.3 参照)も含む。適用可能な利用者定義変換演算子の集合が空の場合,変換元

の型から変換先の型への利用者定義変換は存在しない。

−  適用可能な利用者定義型演算子の集合から,どの演算子があいまいさなく最も限定的かを決定する。

分かりやすくいえば,最も限定的な演算子とは,演算対象の型が変換元の型に“最も近く”

,結果の型

が変換先の型に“最も近い”演算子のことである。最も限定的な利用者定義変換演算子を確定する厳

密な規則については,13.4.313.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)

変換できる。

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 の中の演算子の型のうち,最も限定的な元の型 S

X

を探す。

・  U の中の演算子のいずれかが S からの変換を行う場合,S を S

X

とする。

・  それ以外の場合,U の中の演算子群の変換元の型の結合した集合の中で最も内側の型を S

X

とする。

最も内側の型が見つからない場合又は最も内側の型が一つに決まらない場合,その変換はあいまい

とし,コンパイル時エラーが起こる。

−  次の手順によって,U の中の演算子の型のうち,最も限定的な変換先の型 T

X

を探す。

・  U の中の演算子のいずれかが T への変換を行う場合,T を T

X

とする。

・  それ以外の場合,U の中の演算子群の変換先の型の結合した集合の中で最も外側の型を T

X

とする。

最も外側の型が見つからない場合又は最も外側の型が一つに決まらない場合,その変換はあいまい

とし,コンパイル時エラーが起こる。

−  最も限定的な変換演算子を探す。

・  U が S

X

から T

X

への利用者定義変換演算子を一つだけ含む場合,その演算子を最も限定的な変換演

算子とする。

・  そうでない場合であって,U が SX から TX へのもち上げられた変換演算子を正確に一つだけ含む場

合,その演算子を最も限定的な変換演算子とする。

・  そうでなければ,変換はあいまいとし,コンパイル時エラーになる。

−  最後に,次の手順で変換を適用する。

・  S が S

X

でない場合,S から S

X

への標準暗黙変換を実行する。

・  S

X

から T

X

に変換するために最も限定的な変換演算子を実行する。

・  T

X

が T でない場合,T

X

から 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)

−  適用可能な変換演算子の集合 U を探す。この集合は,D の中のクラス又は構造体によって宣言された,

S

を包含する型若しくは S によって包含される型から T を包含する型若しくは T に包含される型への

利用者定義の暗黙の変換演算子又は利用者定義の明示的な変換演算子,並びに,S 及び T の両方が

null

許容の場合,もち上げられた暗黙又は明示的な変換演算子から成る(13.7.3 参照)

。U が空の場

合,変換は存在せず,コンパイル時エラーが起こるものとする。

−  次の手順によって,U の中の演算子の型のうち,最も限定的な元の型 S

X

を探す。

・  U の中の演算子のいずれかが S からの変換を行う場合,S を S

X

とする。

・  それ以外の場合で,U の中の演算子のいずれかが S を包含する型からの変換を行う場合,これらの

演算子の元の型の結合した集合の中で最も内側の型を S

X

とする。最も内側の型が見つからない場合

又は最も内側の型が一つに決まらない場合,その変換はあいまいとし,コンパイル時エラーが起こ

るものとする。

・  それ以外の場合,U の中の演算子の元の型の結合した集合の中で最も外側の型を S

X

とする。最も外

側の型が見つからない場合又は最も外側の型が一つに決まらない場合,その変換はあいまいとし,

コンパイル時エラーが起こるものとする。

−  次の手順によって,U の中の演算子の変換先の型のうち,最も限定的な変換先の型 T

X

を探す。

・  U の中に T への変換を行う演算子が存在する場合,T を T

X

とする。

・  それ以外の場合で,U の中に T に包含される型への変換が存在する場合,それらの演算子の変換先

の型の結合した集合の中で最も外側の型を T

X

とする。最も外側の型が見つからない場合又は最も外

側の型が一つに決まらない場合,その変換はあいまいとし,コンパイル時エラーが起こるものとす

る。

・  それ以外の場合,U の中の演算子の変換先の型の結合した集合のうち最も内側の型を T

X

とする。最

も内側の型が見つからない場合又は最も内側の型が一つに決まらない場合,その変換はあいまいと

し,コンパイル時エラーが起こるものとする。

−  最も限定的な変換演算子を探す。

・  U が S

X

から T

X

への変換を行う利用者定義変換演算子を一つだけ含む場合,その演算子を最も限定

的な変換演算子とする。

・  それ以外の場合で,U が S

X

から T

X

に変換するもち上げられた変換演算子を一つだけ含む場合,そ

れを最も限定的な変換演算子とする。

・  それ以外の場合,変換はあいまいとし,コンパイル時エラーが起こるものとする。

−  最後に,変換を適用する。

・  S が S

X

でない場合,S から S

X

への標準明示変換を実行する。

・  S

X

から T

X

への変換を行う最も限定的な変換演算子を実行する。

・  T

X

が T でない場合,T

X

から 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)

・  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)

 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)

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.113.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.113.1.213.1.313.1.713.2.1 及び 13.2.2 参照)を行うあらかじめ定義された暗黙の変換又

は明示的な変換のそれぞれについて,次の null 許容変換が存在する。

−  S?から T?への暗黙の変換及び明示的な変換

−  S から T?への暗黙の変換及び明示的な変換


160 
X 3015:2008 (ISO/IEC 23270:2006)

−  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)

−  無名メソッド。この分類による式は,≪委譲生成式≫(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)

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)

同じ優先順位の二つの演算子の間に演算対象がある場合は,次に述べる演算子の結合規則によって,演

算の実行順序を制御する。

−  代入演算子及び 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)

演算子記法

関数記法

op

x operator

 op

(x)

x

 op

 operator

 op

(x)

x

 op

y operator

 op

(x,

y)

利用者定義演算子の宣言では,常に,少なくとも一つの仮引数で,その演算子の宣言を含むクラス型又

は構造体型を指定する必要がある。

注記  したがって,利用者定義演算子があらかじめ定義された演算子と同じ呼出し情報をもつことは

できない。

利用者定義演算子の宣言では,演算子の構文,優先順位又は結合規則を変更することはできない。

例  演算子/は,常に 2 項演算子であり,14.2.1 で示す優先順位をもち,常に左結合とする。

注記  利用者定義演算子では,どのような計算でも実行できるが,直感的に予想されるものと異なる

結果を生成するような実装は避けることを強く推奨する。例えば,演算子==の実装は,等価の

ための二つの演算対象の比較及び適切な結果 bool の返却にすることを推奨する。

14.514.14 での各演算子の記述は,演算子のあらかじめ定義された実装及び各演算子に適用される任意

の追加的規則を規定する。記述中では,単項演算子多重定義解決,2 項演算子多重定義解決及び数値昇格

という用語を使う。14.2.314.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  項演算子多重定義解決

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)

候補から取り除かれる。同様に,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)

14.2.6.1  単項数値昇格

14.2.6.1 は,参考とする。

あらかじめ定義された単項演算子+,単項演算子–及び単項演算子~の演算対象に対して,単項数値昇格

が発生する。単項数値昇格では,sbyte,byte,short,ushort,又は char の各型の演算対象を int

型に変換する。さらに,単項演算子–に対しては,単項数値昇格は,uint 型の演算対象を long 型に変換

する。

14.2.6.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)

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)

それらの宣言内で指示された型仮引数の数をもち,

それ以外のメンバは 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 がメソッドの場合,インタフェース宣言インタフェースで宣言されるメソッド以外のすべてのメ

ンバを集合から取り除く。

−  最後に,隠ぺいされたメンバを取り除いて,検索の結果を次のとおり決定する。

・  集合に含まれているのが,メソッド以外のメンバ一つだけである場合は,そのメンバが検索の結果

となる。

・  そうでなくて,集合に含まれているのがメソッドだけの場合は,そのメソッド群が検索の結果とな