第7回 2.例外

例外処理というのは、なんらかの異常や条件を満たしていなかったときに行う処理のこ
とです。
例外を投げると、続く処理をやめ、すぐに例外処理に飛びます。
Cでは、例えば複数のネストのなかにあるループの処理中に異常が発生した場合、どう
やって抜けるか、gotoを使うべきか、などといった話題が出たものですが、C#ではtry/
chatchという異常時に行う命令が追加されています。

例えば、Cでは以下のような場合gotoを使わないとかなり面倒な処理をしなければなりま
せん。
while( 1 )
{
   while( 1 )
   {
      //ここで問題が発生 ループから抜けたい
   }
}

C#なら、例外を投げるだけですみます。
上記のような特殊ケースはもとより、異常発生時は続く処理を行わないというのは殆ど
の場合当てはまるケースです。
なので、アプリケーションを作成していると、例外処理を書かないというのはあまり無
いといっていいでしょう。

私はコピープログラムで以下のようなコードを書きました。

       private void CopyFile(string src, string dest)
       {
           StreamReader sr = new StreamReader(src , Encoding.GetEncoding("shift_jis"));
           StreamWriter sw = new StreamWriter(dest, false, Encoding.Unicode);

           while (!sr.EndOfStream )
           {
               string ReadText;
               ReadText = sr.ReadLine();
               sw.WriteLine(ReadText);
               sw.Flush();
           }
           sr.Close();
           sw.Close();
       }
       private void button1_Click(object sender, EventArgs e)
       {
           if (!textBox1.Text.Equals("")
           && !textBox2.Text.Equals(""))
           {
               if (File.Exists(textBox1.Text))
               {
                   CopyFile(textBox1.Text, textBox2.Text);
               }
               else
               {
                   MessageBox.Show("コピー元のファイルは存在しないか、パスが不正です");
               }
           }
           else
           {
               MessageBox.Show("コピー元/コピー先のファイル名を入力してください");
           }
       }

ボタンクリックイベントの処理は、間違いではないですが、ちょいと見づらいコードです。
例外処理を使うと、こんな感じになります。

       private void button1_Click(object sender, EventArgs e)
       {
           try
           {
              if ( textBox1.Text.Equals("")
              ||   textBox2.Text.Equals(""))
              {
                  throw new Exeption("コピー元/コピー先のファイル名を入力してください");
              }
              if( (File.Exists(textBox1.Text) )
              {
                  throw new Exeption("コピー元のファイルは存在しないか、パスが不正です");
              }
              CopyFile(textBox1.Text, textBox2.Text);
           }
           catch( Exeption ex )
           {
              MessageBox.Show( ex.Message );
           }
       }

幾分すっきりしました。
でも、実は、C#の殆どのメソッドは、引数などのチェックを行ってくれます。
そのとき、異常があった場合は例外処理を投げてくれるのです。
つまり、自分でこのようなチェックを行う必要は無かったりします。
そこいらを考慮してプログラムを作成すると……

       private void button1_Click(object sender, EventArgs e)
       {
           try
           {
               CopyFile(textBox1.Text, textBox2.Text );
           }
           catch (ArgumentException ex)
           {
               MessageBox.Show( ex.Message);
           }
       }

で、済みます。
簡単ですね。
エラーメッセージもコンパイラ側が用意してくれたものが表示されるので、意味不明な
エラーにはならないというメリットもあります(知っている人が見ればすぐ分かる)


  • 基本的な使い方
まず、例外が発生する可能性のある場所、つまり、例外処理を行いたい部分をtryで囲み
ます。
そして、tryの下に例外が発生した場合に行う処理を、catchで囲みます。
例外が発生したしないに関わらず行いたい処理があればcatchの下にfinallyで囲みます。

try
{
  //ここに例外が発生する可能性の有り処理を記述
}
catch( Exception ex )
{
  //例外が発生した場合の処理を記述
}
finally
{
  //例外の発生有無にかかわらず行いたい処理をここに記述
  //無くてもかまわない
}

  • 例外を投げる
C#のメソッドで問題が発生した場合、例外を投げてくれるのは先ほど説明しましたが、
例外をプログラマが投げたい場合は、throwキーワードを使用します。
C++とちがって、文字列や数値型を投げることは出来ません。
例外用クラスが用意されているので、それを使います。
例外用クラスは、基本クラスのExeptionや、引数が異常のときに投げるArgumentExeption
等、目的によって使うクラスを使い分けることが出来ます。
例外を投げる際は、インスタンス化してある例外クラスを投げるか、throwキーワードの
後にnewキーワードを用います。
コンストラクタは幾つかオーバーロードされています。
これについてはの地ほど説明を行います。

  • 例外を受け取る
投げた例外は、catchキーワードのカッコ内で、対象の例外クラスと、オブジェクト名を
記述して受け取ります。
C++と違って、どのような例外でも受け取る「...」は使えません。
また、catchは複数記述することが出来ます。
例えば、引数例外であるArgumentExeptionと、システム例外であるSystemExeptionで、
異なる処理を記述できます。

メソッド内にcatchキーワードが無い場合、メソッドの呼び出し元に例外の発生を知らせ
ます。
ファイルコピーの例を見てください。
例外が発生するのはCopyFileメソッドですが、例外を受け取って処理を行っているのは
button1_Clickイベントです。

private void Method()
{
   throw new Exception();  //例外を投げる
}
private void button1_Click(object sender, EventArgs e)
{
   try
   {
      Method();   //呼び出し元
   }
   catch( Exception ex )
   {
       //Methodで投げた例外を処理
       MessageBox.Show("ボタンクリックイベントで例外を処理");
   }
}

catchの中で、更に例外を投げることも出来ます。
これは、メソッド内で例外処理を行った後、メソッドの送信元でも例外処理を行いたい
時などに使用します。
その際は、投げる例外を省くと(つまり、throwだけを記述すると)受け取った例外をそ
のまま投げてくれます。

private void Method()
{
   try
   {
      throw new Exeption();  //例外を投げる
   }
   catch( Exception ex )
   {
      //例外を処理する
      MessageBox.Show("メソッドで例外を処理");
      throw;  //ここで受け取った例外を更に投げる
   }
}
private void button1_Click(object sender, EventArgs e)
{
   try
   {
      Method();   //呼び出し元
   }
   catch( Exception ex )
   {
       //Methodのcatchで投げた例外は、ここで処理される
       MessageBox.Show("ボタンクリックイベントで例外を処理");
   }
}

  • 共通処理
例外が発生した・しないにかかわらず行いたい処理があればfinallyの括弧内に処理を
記述します。
C++のようにcatchの後にコードを書いてもほぼ同じように動作しますが、finallyは、
catchの中で、例外を投げてもfinallyに記述したコードが実行されるという点が異なり
ます。

例えば、以下の例では「共通の処理?」は表示されません。
private void Method()
{
   try
   {
      throw new Exeption();
   }
   catch( Exception ex )
   {
      throw;
   }
   MessageBox.Show("共通の処理?");
}
private void button1_Click(object sender, EventArgs e)
{
   try
   {
      Method();
   }
   catch( Exception ex )
   {
       MessageBox.Show("ボタンクリックイベントで例外を処理");
   }
}

Methodを以下のようにすれば、メッセージが表示されます。
private void Method()
{
   try
   {
      throw new Exception();
   }
   catch( Exception ex )
   {
      throw;
   }
   finally
   {
       MessageBox.Show("共通の処理!");
   }
}

  • 例外クラス
例外クラスのコンストラクタは幾つかオーバーロードされていますが、よく使うのはパ
ラメータが空のものと、文字列を渡すものでしょう。
文字列を渡すと、Messageプロパティに渡した文字がセットされます。

private void button1_Click(object sender, EventArgs e)
{
   try
   {
      throw new Exception("例外発生");
   }
   catch( Exception ex )
   {
       MessageBox.Show( ex.Message );
   }
}

例外クラスの派生クラス、例えばArgumentException等はコンストラクタやプロパティが
追加されていたりしますが、基本的な使い方は一緒です。
最終更新:2007年10月29日 12:47
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。