DOBON.NET DOBON.NETプログラミング掲示板過去ログ

例外処理について

環境/言語:[XP VB2005]
分類:[.NET]

ファイルをStreamReaderで読み込むさい、
もしファイルが存在しない場合例外をキャッチしたいのですが、
次のようにするとsrが宣言されていませんとエラーが出ます。
どのように処理をするべきなのでしょうか…。

Try
Dim sr As New System.IO.StreamReader("file.dat", _
System.Text.Encoding.GetEncoding(65001))

Catch ex As Exception
MessageBox.Show(ex.Message, "例外")
End Try

Dim strLine As String = sr.ReadLine()

sr.Close()
変数 sr のスコープが try catch 内なのが原因ですね。
以下のように記述すると sr が有効になります。

Try
Dim sr As New System.IO.StreamReader("file.dat", _
System.Text.Encoding.GetEncoding(65001))

Dim strLine As String = sr.ReadLine()

sr.Close()

Catch ex As Exception
MessageBox.Show(ex.Message, "例外")
End Try
2006/03/07(Tue) 08:52:49 編集(投稿者)

マツシマ・ジュンさん,こんにちは。

普通は以下のようになると思います。

変数の宣言はTryの前にします。
必ず実行しなければならないCloseはFinallyの中でします。

Dim sr As System.IO.StreamReader = Nothing
Dim strLine As String

Try
sr = New System.IO.StreamReader("file.dat", System.Text.Encoding.GetEncoding(65001))
strLine = sr.ReadLine()

Catch ex As Exception
MessageBox.Show(ex.Message, "例外")

Finally
sr.Close()

End Try
  • 題名: Re[3]: 例外処理について
  • 著者: なおこ(・∀・)
  • 日時: 2006/03/07 9:18:39
  • ID: 15450
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
お世話になります。

■No15449に返信(YASさんの記事)
> sr.Close()

If Not sr Is Nothing Then sr.Close()
だとよりよいかもしれません。
■No15449に返信(YASさんの記事)
> 普通は以下のようになると思います。

VB2005 なので Using も欲しいですね。

___________________________________________________________________________________
じゃんぬ - Microsoft MVP for Visual Developer C#
  C#, VB.NET 入門  http://jeanne.wankuma.com/
  じゃんぬねっと日誌 http://blogs.wankuma.com/jeanne/
■No15450に返信(なおこ(・∀・)さんの記事)

> If Not sr Is Nothing Then sr.Close()
> だとよりよいかもしれません。

そうですね。うっかり忘れてしまいました。
訂正して頂き,ありがとうございます。

よけいなことですが,個人的好みでは
If sr IsNot Nothing Then sr.Close()
です。(^^)

■No15452に返信(じゃんぬねっとさんの記事)

> VB2005 なので Using も欲しいですね。

そうですね。個人的には私もUsingを使います。
(入れ子にするとどんどんインデントするのがあまり好きではないのですが)

でもUsingではFinallyのsr.Close()についてはよいですが,Catchの処理のために
結局Try Catchが必要になり,今回は逆にわかりづらいかなと思って使いませんで
した。
■No15454に返信(YASさんの記事)
> でもUsingではFinallyのsr.Close()についてはよいですが,Catchの処理のために
> 結局Try Catchが必要になり,今回は逆にわかりづらいかなと思って使いませんでした。

例外処理で Catch があるからインデントの関係上 Finally にすべきとか、
Finally があるから Using が必要ないとか、そういう問題ではないと思いますよ。
Dispose - Using, Close - Finally という関係は適切だと思いますから。

既存の Framework であれば等価であると証明しているものもあるので意見が分かれますが、
「そういう意味で述べた」ということが言いたかっただけだったり...

Connection 系だと Open があるので納得しやすいのですが、ね。

___________________________________________________________________________________
じゃんぬ - Microsoft MVP for Visual Developer C#
  C#, VB.NET 入門  http://jeanne.wankuma.com/
  じゃんぬねっと日誌 http://blogs.wankuma.com/jeanne/
  • 題名: Re[6]: 例外処理について
  • 著者: YAS
  • 日時: 2006/03/07 12:52:36
  • ID: 15456
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
> 例外処理で Catch があるからインデントの関係上 Finally にすべきとか、
> Finally があるから Using が必要ないとか、そういう問題ではないと思います
> よ。
> Dispose - Using, Close - Finally という関係は適切だと思いますから。

いや,誤解です。おっしゃることはわかります。
私の言いたかったことは, マツシマ・ジュンさんが,Try Catch Finallyの
よくある使い方をご存じなかったようなので,ここではそれがよくわかる例を
説明した方がよいのではないかと考えた,ということです。

Usingを使うと,典型的なTry Catch Finallyの使い方の説明がわかりにくく
なるのではないか,ということが言いたかったのです。

■No15454 YASの記事
> 普通は以下のようになると思います。

「普通は」は「普通のStreamReaderの使い方」ではなく,「普通のTry Catch
Finallyの記述の仕方」という意味です。

「〜すべき」という点に関しては,まったくじゃんぬねっとさんの
おっしゃる通りだと思います。コードの記述の仕方に関して持論に固執する
つもりはまったくありません。
> 必ず実行しなければならないCloseはFinallyの中でします。

「必ず実行しなければならない Close()」は無いと思います。
僕は、Close() は正常系の処理だと思います。

StreamWriter() なんか、Close() で例外を投げてよこすこともあるわけで、その視点に立つなら、finally ブロックで Close() するなら、さらにそれを try ~ catch するか、using よりも1段深くネストさせなくちゃならないような。。。
(どっちもイヤですが)
■No15462に返信(渋木宏明(ひどり)さんの記事)
>>必ず実行しなければならないCloseはFinallyの中でします。
>
> 「必ず実行しなければならない Close()」は無いと思います。
> 僕は、Close() は正常系の処理だと思います。
>
> StreamWriter() なんか、Close() で例外を投げてよこすこともあるわけで、その視点に立つなら、finally ブロックで Close() するなら、さらにそれを try ~ catch するか、using よりも1段深くネストさせなくちゃならないような。。。
> (どっちもイヤですが)

ご指摘ありがとうございます。
不勉強なもので,Close()はFinallyの中に書くものだとばかり思っていました...
■No15463に返信(YASさんの記事)
> ご指摘ありがとうございます。
> 不勉強なもので,Close()はFinallyの中に書くものだとばかり思っていました...

別に Finally に書くこと自体はダメじゃないと思いますよ。
Using も括ってあるのであれば、例外時にも Dispose はされます。

Using がないのに Finally にだけ Close しているのであれば、それは少なからず危ないと言えます。

___________________________________________________________________________________
じゃんぬ - Microsoft MVP for Visual Developer C#
  C#, VB.NET 入門  http://jeanne.wankuma.com/
  じゃんぬねっと日誌 http://blogs.wankuma.com/jeanne/
> Using がないのに Finally にだけ Close しているのであれば、それは少なから
> ず危ないと言えます。

すると下のようにUsingを使うと言うことは

Using Reader as New StreamReader(...)

End Using

下のようにただFinallyでCloseするだけではなく,

Dim Reader as StreamReader
Try
Reader = New StreamReader(...)
Finally
Reader.Close()
End Finally

渋木宏明(ひどり)さんがおっしゃっていたような,この下のような記述に
近い動作になるということですね。

Dim Reader As StreamReader
Try
Reader = New StreamReader(...)
Finally
Try
Reader.Close()
Finally
Reader.Dispose()'Disposeは例外は発生しないんですよね
End Try
End Try

だんだんUsingのすばらしさがわかってきました。
■No15469に返信(YASさんの記事)
> 渋木宏明(ひどり)さんがおっしゃっていたような,この下のような記述に
> 近い動作になるということですね。

ひどりさんのスタイルでは、Close メソッドが正常系のみになります。(おそらく)

    Using Reader As New StreamReader(...)
        ' Reader の処理
        '      :
        reader.Close()
    End Using

例外発生時は Dispose のみに一任するという解釈です。
私はおそらく YAS さんスタイルです。

このあたりは宗教論争と一緒で、正しいとか誤っているとかはないですよね。
わかっていて使っている、というのさえあれば OK だと思います。

________________________________________________________
じゃんぬ - Microsoft MVP for Visual Developer C#
  C#, VB.NET 入門     http://jeanne.wankuma.com/
  じゃんぬねっと日誌  http://blogs.wankuma.com/jeanne/
2006/03/08(Wed) 02:23:18 編集(投稿者)

じゃんぬねっとさん,渋木宏明(ひどり) さん,ありがとうございました。

■No15446に返信(マツシマ・ジュンさんの記事)
> ファイルをStreamReaderで読み込むさい、
> もしファイルが存在しない場合例外をキャッチしたいのですが、
> 次のようにするとsrが宣言されていませんとエラーが出ます。
> どのように処理をするべきなのでしょうか…。

では,最初の質問に戻ると,

Dim sr As System.IO.StreamReader
Try
sr = New System.IO.StreamReader("file.dat", System.Text.Encoding.GetEncoding(65001))
Dim strLine As String = sr.ReadLine()
sr.Close()
Catch ex As Exception
MessageBox.Show(ex.Message, "例外")
Finally
If sr IsNot Nothing Then sr.Dispose()
End Try

または,

Using sr As New System.IO.StreamReader("file.dat", System.Text.Encoding.GetEncoding(65001))
Dim strLine As String = sr.ReadLine()
sr.Close() '<- あってもなくてもよい
End Using

という感じでしょうか。
議論が白熱しているようですね。
マツシマさんの疑問は、"sr が見つからない" というエラーが出るのでどうしたら良いか、、ということでしたので、
私ははじめのほうのレスで変数のスコープが有効になるように try catch 内に単純にコピーアンドペーストしただけですが、
私の記述だと、Dim strLine As String = sr.ReadLine() 行で例外が発生した場合、
sr が Close されず、以下のように問題があります。( 変数 sr の catch 後の状態をわかりやすくするため、コメントを入れてみました。 )
* C# で記述しました

try
{
  StreamReader sr = new StreamReader( "test.txt" );

  throw new Exception(); // string strLine = sr.ReadLine(); で例外が発生したと仮定するため置き換え
  
  sr.Close();
}
catch
{
  // sr は Close されていません。または、
  // インスタンス作成時に例外が発生したため、null です。
}



例外も考えて Close メソッドで sr 処理する場合は、以下のようになると思います。

StreamReader sr = null;
try
{
  sr = new StreamReader( "test.txt" );
  throw new Exception();
}
catch
{
  if ( sr != null )
  {
    sr.Close();
    // sr は Close されて、null です。
  }
  else
  {
    // sr はインスタンス作成時に例外が発生したため、null です。
  }
}



using で Dispose する場合は、以下のようになります。

try
{
  using ( StreamReader sr = new StreamReader( "test.txt" ) )
  {
    throw new Exception();
  }
}
catch
{
  // sr は using によって Dispose されて null です。または
  // インスタンス作成時に例外が発生したため、null です。
}



using を使用する場合、呼び出される StreamReader.Dispose メソッドの内部での処理は Close メソッドと同等なので、
Closeメソッドを記述する必要はありません。

こうして Close と using で記述して比較してみると変数 sr のスコープを狭く定義出来て、シンプルに記述できる using が良さそうですね。
■No15475に返信(caferaさんの記事)
> こうして Close と using で記述して比較してみると変数 sr のスコープを狭く定義出来て、
> シンプルに記述できる using が良さそうですね。

1 番最後のソースは、"機構自体が違う" ので、
その前にある 2 つとスコープの比較対照をするのはおかしいですよね。

> using で Dispose する場合は、以下のようになります。
> 
> try
> {
>   using ( StreamReader sr = new StreamReader( "test.txt" ) )
>   {
>     throw new Exception();
>   }
> }
> catch
> {
>   // sr は using によって Dispose されて null です。または
>   // インスタンス作成時に例外が発生したため、null です。
> }

これとほぼ等価な try 〜 finally パターンはこうです。

    try {
        StreamReader sr = null;

        try {
            sr = new StreamReader("test.txt");
            throw new Exception();
        } finally {
            if (sr != null) {
                ((System.IDisposable)sr).Dispose();
            }
        }
    } catch {
         // 何らかの例外が発生した
    }

こちらと比較して「シンプルで良いですね」なら良いのです。
ただし理由は 「finally に "わざわざ記述しなくて良いので"」に過ぎません。

> catch
> {
>   // sr は using によって Dispose されて null です。または
>   // インスタンス作成時に例外が発生したため、null です。
> }

3 番目のここですが、「インスタンス作成時に例外が発生したため」とは限りませんね。
ここでの catch 句のスコープを考えてみてください。
(実際、現状のコードは「インスタンス作成時」以外で発生していますし)

> sr のスコープを狭く定義出来て、シンプルに記述できる using が良さそうですね。

とありますが、例外機構の範囲を長くすることのデメリットは無視するのでしょうか?
■No15472に返信(YASさんの記事)
> では,最初の質問に戻ると,
> (code snip)
> または,
> (code snip)
> という感じでしょうか。

【私の場合】

  [Using パターン]

    Using sr As New System.IO.StreamReader(...)
        Try
              :
              :
              :
        Finally
            If sr IsNot Nothing Then
                sr.Close()
            End If
        End Try
    End Using

  [Try 〜 Finally パターン]

    Dim sr As System.IO.StreamReader 

    Try
        sr = New System.IO.StreamReader(...)

        Try
            :
            :
            :
        Finally
            If sr IsNot Nothing Then
                sr.Close()
            End If
        End Try
    Finally
        If sr IsNot Nothing Then
            sr.Dispose()
        End If
    End Try

【ひどりさんの場合】

  [Using パターン]

    Using sr As New System.IO.StreamReader(...)
           :
           :
        sr.Close()   ' 正常系のみ (例外が発生した場合は Dispose はされるので大丈夫)
    End Using

ということでしょう。
宗教的なものであり、どちらも間違いではありません。
> 1 番最後のソースは、"機構自体が違う" ので、
> その前にある 2 つとスコープの比較対照をするのはおかしいですよね。

機構自体違うというのは、どういう意味でしょう。
ただ try catch 内で using で sr を宣言して扱っているだけなのですが。。



> こちらと比較して「シンプルで良いですね」なら良いのです。
> ただし理由は 「finally に "わざわざ記述しなくて良いので"」に過ぎません。

示されたコードは using を使わずに using と同様の処理をコード上で再現されているんですよね。
興味深く拝見させていただきました。
ですが、指摘されている意図がちょっとよくわかりませんでした。



> 3 番目のここですが、「インスタンス作成時に例外が発生したため」とは限りませんね。
> ここでの catch 句のスコープを考えてみてください。
> (実際、現状のコードは「インスタンス作成時」以外で発生していますし)

try
{
  using ( StreamReader sr = new StreamReader( "test.txt" ) )
  {
    throw new Exception();
  }
}
catch
{
  // sr は using によって Dispose されて null です。または
  // インスタンス作成時に例外が発生したため、null です。
}

このコード上で、catch される例外は sr のインスタンス作成時( ファイルが存在しなかった等 )と、
自前で発生させた例外のスローのみですよね。

ということで、catch 内に到達したときの sr の状態の説明は適切だと思うんですが。。



> 例外機構の範囲を長くすることのデメリットは無視するのでしょうか?

例外機構というのがどの範囲を示されているのかはっきりわかりませんが、
Close メソッドのコードと using を使用したコードにあるスコープの違う要素は変数 sr だけですよね?

変数のスコープは出来る限り狭くなるように、宣言がしたほうが良いように思うので、
変数を try catch 内のスコープに納めた形で記述できる using が良いと考えました。
■No15482に返信(caferaさんの記事)
> 機構自体違うというのは、どういう意味でしょう。
> ただ try catch 内で using で sr を宣言して扱っているだけなのですが。。

機構というのはブロック、そして例外ハンドラの範囲ですね。

> > これとほぼ等価な try 〜 finally パターンはこうです。

と、比べる "べき" パターンを既に書いていますので、説明は端折りました。
(と書いておかないと、何を指摘しているのかわかりにくいかもしれませんね、すいません)

> > こちらと比較して「シンプルで良いですね」なら良いのです。
> > ただし理由は 「finally に "わざわざ記述しなくて良いので"」に過ぎません。
> ですが、指摘されている意図がちょっとよくわかりませんでした。

上にも書きましたが、比べる "べき" パターンが誤っていることを指摘しています。

2 行目にある「わざわざ記述しなくても良い」というのは、私の「シンプル」の解釈です。
こちらに関しては指摘ではありません。

指摘したいのは、「こちらのパターン」と比べないと、
結論として書いたものが「結論」になりませんよ、ということです。

> このコード上で、catch される例外は sr のインスタンス作成時( ファイルが存在しなかった等 )と、
> 自前で発生させた例外のスローのみですよね。
> ということで、catch 内に到達したときの sr の状態の説明は適切だと思うんですが。。

あちゃー「または」を見逃してました。(;__ __)

try のブロックが using の外に出ていますので、別の処理が入れば、
その説明文は不適切に成りえる可能性はありますが、
とりあえず、"そのコード" のままであれば、そのままで正しいです。

これは私の読み違いです、すいません。(*__ __)

> 例外機構というのがどの範囲を示されているのかはっきりわかりませんが、

例外機構は 1 つしかありませんよね。
try { 〜 } catch { } 間のことです。

using 内で例外処理を実装した場合に比べて、スコープは広くなります。
そのデメリットは無視するのでしょうか? と、述べたつもりです。

このデメリットとして、catch 句に将来的に改変によって
別の例外が入りやすくなる余地 (または、そうならないようにする手間) があることも含まれます。

> Close メソッドのコードと using を使用したコードにあるスコープの違う要素は変数 sr だけですよね?
> 変数のスコープは出来る限り狭くなるように、宣言がしたほうが良いように思うので、
> 変数を try catch 内のスコープに納めた形で記述できる using が良いと考えました。

変数のアクセス範囲を狭めるという点は正しいのですが、
例外ハンドラの有効範囲も狭いに越したことはありません。

変数のアクセス範囲の最適化 "だけ" を見ている == 例外ハンドラの有効範囲は無視している。と述べています。
両方を加味していれば、「こっちの方が絶対良い」という理由付けが、また別で必要になるでしょう。

かなり噛み砕いたつもりですが、わかりにくかったらすいません。
自分で話を広げておいて、なんですけど、
私のような小者に聞くより、CodeComplete とか読んだ方が絶対良いでしょうね... orz
> try
> {
>   using ( StreamReader sr = new StreamReader( "test.txt" ) )
>   {
>     throw new Exception();
>   }
> }
> catch
> {
>   // sr は using によって Dispose されて null です。または
>   // インスタンス作成時に例外が発生したため、null です。
> }

catch ブロックのコメントが間違っていますよ。

「sr はスコープが異なるから」見えません。

また、Dispose() がインスタンス参照を null にすることはありません。
インスタンスを ObjectDisposed な状態に陥らせる可能性があるだけです。
> このあたりは宗教論争と一緒で、正しいとか誤っているとかはないですよね。
> わかっていて使っている、というのさえあれば OK だと思います。

「Close() が例外を発生することもある」のを踏まえたネスト構造になっていれば、いいんじゃないでしょうか。
  • 題名: Re[5]: 例外処理について
  • 著者: cafera
  • 日時: 2006/03/08 20:07:36
  • ID: 15494
  • この記事の返信元:
  • この記事への返信:
    • (なし)
  • ツリーを表示
> catch ブロックのコメントが間違っていますよ。
> 「sr はスコープが異なるから」見えません。

本当ですね、null という説明はおかしいですね。もう catch に入った時点で sr はどこからも見えなくなっていますもんね。
しかもさっき自分自身でコードをテストしてみたとき、Dispose された後、null になっているように見えて、
Dispose == null という事だと勘違いしてコードを記述していました。

ああ、なんだか混乱させるようなことを書いてすみません。。
■No15493に返信(渋木宏明(ひどり)さんの記事)
> 「Close() が例外を発生することもある」のを踏まえたネスト構造になっていれば、いいんじゃないでしょうか。

ですね。
極論である、Dispose さえ保証されていれば良いというのも「アリ」でしょう。
私は精神衛生上できない人なんですけど。

どこの掲示板を行っても、IDisposable ネタは熱いですね。
> 極論である、Dispose さえ保証されていれば良いというのも「アリ」でしょう。
> 私は精神衛生上できない人なんですけど。

逆に僕は、正常系の処理である(はずの) Close() が例外処理ブロックに追いやられるのには違和感を感じます。

「そんなに Close() って例外的なん?」という感じがして。。。 (^^;
■No15497に返信(渋木宏明(ひどり)さんの記事)
> 逆に僕は、正常系の処理である(はずの) Close() が例外処理ブロックに追いやられるのには違和感を感じます。
> 「そんなに Close() って例外的なん?」という感じがして。。。 (^^;

例外処理ブロックと書くと誤解されるような... Finally ですよね。
例外的というよりは、「何が何でも Close する」という感じだと思います。

DOBON.NET | プログラミング道 | プログラミング掲示板