繰り返しのコントロール
繰り返しは、律儀に指定された回数だけ必ず最後まで実行しなければならないものではないし、処理内容を必ず最後まで実行しなければならないものでもない。
繰り返しをコントロールする手段が2つある。1つはcontinueステートメントによって、処理を打ち切って繰り返しを続行する方法。もう1つは、breakステートメントによる繰り返しからの脱出である。以下のサンプルソースを見ていただきたい。基本的には、9行目のforステートメントによって、19行目の出力を繰り返し実行させようとしている。そこで、11〜18行目の記述内容が割り込んで、繰り返しを意識的にコントロールするように組まれている。まず最初に、11〜14行目の記述により、変数iの値が4より小さい場合は、continueが実行される。continueはそこで処理を打ち切って繰り返しを続けるので、contineが実行されると15〜19行目が実行されることはない。変数iが4以上になると、continueは実行されなくなり、19行目が実行され、数値がコンソールに出力される。しかし、変数iが7に達すると、17行目のbreakが実行される。breakは繰り返しをその場で終了させる働きがあるので、breakが実行された瞬間に21行目にジャンプする。そのため、変数iが8や9になることはない。
1: namespace ConsoleApplication15
2: {
3: using System;
4:
5: public class Class1
6: {
7: public static int Main(string[] args)
8: {
9: for( int i=0; i<10; i++ )
10: {
11: if( i<4 )
12: {
13: continue;
14: }
15: if( i==7 )
16: {
17: break;
18: }
19: Console.WriteLine(i);
20: }
21: return 0;
22: }
23: }
24: }
|
|
繰り返しを制御するためにcontinueとbreakを用いたサンプル・プログラム |
このサンプルソースを実行した結果は以下のようになる。
|
プログラムの実行結果 |
変数の値が4より小さい場合にはcontinueステートメントにより繰り返しを続行し、7になった時点で繰り返しから抜け出す。
|
メソッドからの脱出
繰り返しから脱出するbreakと似た機能として、メソッドからの脱出となるreturnというステートメントがある。returnは、値を返すメソッドで戻り値となる値を指定する機能があると共に、メソッドの中間で使うと、そこでメソッドの実行を打ち切って、呼び出し側に戻るという機能を持つ。
以下のサンプルソースを見ていただきたい。11行目のforで10回繰り返しを行い、15行目の“Console.WriteLine(i);”によって何回目を実行中か出力するようになっている。そして、指定回数の繰り返しが終了すると、17行目で「All Done」と出力して終わる。しかし、11〜14行目のコードが存在することによって、動作が変わる。11行目の条件により、変数iが7に達すると、13行目のreturnステートメントが実行され、そこでメソッドの実行は終了する。そのため、変数iが8や9になることがないだけでなく、15行目のConsole.WriteLineメソッドが呼び出されることも永遠にない。
なお、returnを含むメソッドに戻り値がない場合は、returnの後に式は書かない。戻り値がない場合、returnを書かねばならないのは中途脱出を意図した場合だけで、メソッドの最後で終わるなら、特にreturnを書かなくてもよい。
1: namespace ConsoleApplication15
2: {
3: using System;
4:
5: public class Class1
6: {
7: public static int Main(string[] args)
8: {
9: for( int i=0; i<10; i++ )
10: {
11: if( i==7 )
12: {
13: return 0;
14: }
15: Console.WriteLine(i);
16: }
17: Console.WriteLine("All Done");
18: return 0;
19: }
20: }
21: }
|
|
returnにより繰り返しの途中でメソッドを抜け出すサンプル・プログラム |
このサンプルソースを実行した結果は以下のようになる。
|
プログラムの実行結果 |
変数の値が7になった時点でreturnによりメソッドの実行が終わるため、残りの処理は実行されない。
|
オーバーフローチェック
C#では基本的にオーバーフローのチェックを行わないとすでに説明したが、あくまでそのまま何もせず使えばと言うことであって、明示的に指定すればオーバーフロー・チェックもできる。そのための手段の1つが、checked、 uncheckedステートメントである。
checkedステートメントで指定されたステートメントの中で計算が行われるとき、オーバーフローは例外としてスローされる(例外についてはこの連載でいずれ解説する)。uncheckedステートメントはcheckedの反対で、オーバーフローのチェックを行わない。
実際にこのステートメントを使った例を以下に見ていただきたい。10、15、21行目ではオーバーフローが発生するようになっている。10行目は何も指定していないので、チェックは行われず、変な値になってしまう。15行目はuncheckedステートメントの効力範囲内なので、もちろん、オーバーフローはチェックされない。21行目は、checkedステートメントの効力範囲内なので、オーバーフローがチェックされ、例外が発生する。リストの下に掲載した画面スナップショットを見ると、例外がスローされていることが分かると思う。
1: namespace ConsoleApplication19
2: {
3: using System;
4:
5: public class Class1
6: {
7: public static int Main(string[] args)
8: {
9: int i=1;
10: i = i + int.MaxValue;
11: Console.WriteLine(i);
12:
13: unchecked {
14: int j=1;
15: j = j + int.MaxValue;
16: Console.WriteLine(j);
17: }
18:
19: checked {
20: int k=1;
21: k = k + int.MaxValue;
22: Console.WriteLine(k);
23: }
24:
25: return 0;
26: }
27: }
28: }
|
|
オーバーフロー制御のためのunchecked、checkedを使用したサンプル・プログラム |
このサンプルソースを実行した結果は以下のようになる。
|
プログラムの実行結果 |
オーバーフローをチェックしない場合、int型の最大値であるint.MaxValueに1を加えているため、オーバーフローが発生し-2147483648という変な値が表示される。一方、オーバーフローをチェックした場合には例外がスローされ、プログラムが中断される(通常このような場合には例外処理を追加し、オーバーフロー時に対応した処理を記述する)。
|
ラベルとジャンプ
処理されるステートメントの順番を強引にねじ曲げる究極の方法が存在する。ステートメントにはラベルというものを付けることができ、そのラベルに対して制御を移すgotoステートメントが存在する。強力すぎてたいへん危険な機能であり、利用することはあまりお勧めできない。だが、この機能を使った方が綺麗に読みやすくなる場合も希に存在するので、機能として存在するものと思われる。
このサンプルソースは、コマンドライン引数の数が1の場合は、11行から18行に一気に途中を飛ばして処理がジャンプする。2の場合は、15行から19行にジャンプする。いずれでもない場合はそのまま処理が進み、17〜19行の出力が行われてから終了する。
1: namespace ConsoleApplication14
2: {
3: using System;
4:
5: public class Class1
6: {
7: public static int Main(string[] args)
8: {
9: if( args.Length == 1 )
10: {
11: goto x;
12: }
13: if( args.Length == 2 )
14: {
15: goto y;
16: }
17: Console.WriteLine("without labels");
18: x: Console.WriteLine("with label x");
19: y: Console.WriteLine("with label y");
20: return 0;
21: }
22: }
23: }
|
|
gotoとラベルを使用したサンプル・プログラム |
このサンプルソースを実行した結果は以下のようになる。
|
プログラムの実行結果 |
プログラムの引数の数が1つの場合にはラベルxに制御がジャンプし、2つの場合にはラベルyにジャンプする。
|
まとめ
今回紹介したステートメントは、C#のすべてのステートメントではない。しかし、この範囲だけ見ても、C/C++から継承したもの、Visual BASICライクなもの、まったく新しく生まれてきたものとバリエーション豊富である。過去にどんなプログラム言語を経験してきたにせよ、C#ではそれらとは少し違ったコーディングを行うことができる。ぜひとも、慣れた方法にこだわるよりも、C#ならではのエレガントな書き方を活用しようではないか。
さて次回は名前空間(namespace)について取り上げたいと考えている。
それでは次回もLet's See Sharp!