Skip to main content.

Wednesday, September 05, 2007

C#基礎・usingステートメント

C#でのusingステートメントについてと例外処理についてです。下のプログラムは、C#基礎・ファイル操作で書いたプログラムです。これでは実際には不完全なエラー処理となっています。
 //ファイルから読み込む
    try{
        StreamReader reader = new StreamReader(oDialog.FileName,System.Text.Encoding.GetEncodin("Shift_JIS"));
        this.textBoxEdit.Text = reader.ReadToEnd();
        reader.Close();
    }
    catch (Exception ex){
        String msg;
        msg = "ファイル読み込み時にエラーが発生しました。\n" + ex.Message;
        MessageBox.Show(msg);
    }

上の処理では、tryのスコープにClose()の処理があるので、場合によってはClose()の処理が実行されないまま終了してしまいます。それでは問題があるので、finallyを使って以下のように記述します。
    StreamReader reader = null;
                
    //ファイルから読み込む
    try
    {
        reader = new StreamReader(oDialog.FileName, System.Text.Encoding.GetEncoding("Shift_JIS"));
        this.textBoxEdit.Text = reader.ReadToEnd();
        //reader.Close();
    }
    catch (Exception ex)
    {
        String msg;

        msg = "ファイル読み込み時にエラーが発生しました。\n" + ex.Message;
        MessageBox.Show(msg);
    }
    finally
    {
        //例外が発生しない場合でもfinallyは必ず実行されるので
        //リソースの解放はfinallyで行う
        if(reader != null){
            reader.Close();
        }
    }

このような記述は、一般的にはファイルやデータベースコネクションなど、アンマネージなリソース(マネージリソースはガベージコレクタが解放してくれる。)を使う場合の基本的な記述です。これをusingステートメント(※名前空間の記述のusingディレクティブとは全く別のもの)を使用すると、以下のように記述出来ます。
    StreamReader reader = null;

    using( reader = new StreamReader(oDialog.FileName, System.Text.Encoding.GetEncoding("Shift_JIS")) ){
        this.textBoxEdit.Text = reader.ReadToEnd();
    }

usingステートメントとは、MSDNの解説からそのまま引用させていただくと以下の説明になります。

「using ステートメントでインスタンスを作成して、using ステートメントの終了時に Dispose メソッドが呼び出されることを保証します。using ステートメントが終了するのは、using ステートメントの末尾に到達したときか、例外がスローされたなどの理由で、ステートメントの末尾に到達する前に制御がステートメント ブロックを離れたときです。

インスタンスを作成するオブジェクトには、System.IDisposable インターフェイスが実装されている必要があります。」

StreamReaderについて考えると、StreamReaderではその親であるTextReaderがIDisposableを実装しています。また、StreamReaderクラスのCloseメソッドはMSDNの解説によると、「このCloseの実装は、true値を渡すDisposeメソッドを呼び出します。」とあります。つまり、上のusingを使った例では、正常時でも例外時でも自動的にDispose(Closeと実質は同じ)が呼び出されるということです。

ただし、上のusingを使った例では、例外発生時でもDisposeが呼ばれるにしても、例外のメッセージ表示等がないので、実際には以下のようになるでしょうか。
    StreamReader reader = null;

    try{
        using( reader = new StreamReader(oDialog.FileName, System.Text.Encoding.GetEncoding("Shift_JIS")) ){
            this.textBoxEdit.Text = reader.ReadToEnd();
        }
    }
    catch (Exception ex)
    {
            String msg;

            msg = "ファイル読み込み時にエラーが発生しました。\n" + ex.Message;
            MessageBox.Show(msg);
    }

これで、finallyを使った場合と同じような処理になると思います。但し、finallyは書かなくてもいいことになりますので、その分すっきりするように思えますが、いかがでしょうか。
C#基礎・ファイル操作で書いたプログラムをusingに書き換えたソースのプロジェクト一式は以下です。
プロジェクト一式

参考リンク
using ステートメント
アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装

Windowsプログラミング関連記事