2009年4月12日日曜日

[.NET]例外の再スロー(Throwの引数の有無)

[はじめに]
・.NET系言語(C#、VB.NET)のプログラムコードで、
 以下のような構文を見かけることがあります。
  Try 
    Dim x As Integer = func2(a) 
    Return x 
  Catch ex As Exception 
    '何らかの例外後の後処理をして、再スロー 
    '(例外後の処理は割愛)... 
    Throw ex    '←ここに注目 
  End Try
[VB.NET]例外の再スロー[Throwの引数がある場合]
 Tryステートメント内で発生した例外をCatchして、
 再度スローする処理で、「Throw ex」と書く人が結構多いようですが、
 実はこのような書き方をすると、
 ここでCatchする以前のトレース情報が失われてしまうのです。

 トレース情報を保持しつつ再スローするには、
 以下の様に、「Throw ex」ではなく「Throw 」と記述します。
  Try 
      Dim x As Integer = func2(a) 
      Return x 
  Catch ex As Exception 
      '何らかの例外後の後処理をして、再スロー 
      '(例外後の処理は割愛)... 
      Throw    '←exは省略します。 
  End Try 
[VB.NET]例外の再スロー[Throwの引数がない場合]

 なぜか書籍やMSDNにも、前者の書き方でサンプルを紹介している為、
 知らない人が多いようです。
 (Throwステートメントとは直接関係ないサンプルだから、
  あまり重要視していないのだと思いますが…)
[参考文献]
「C#クックブック 第3版」
 「レシピ4.1 キャッチした例外を再スローするタイミングを把握する」で、
 Throwステートメントの引数の有無の違いを明確に説明しています。
 「Throw」(引数なし)の記述を推奨しています。
「プログラミングC#―C#2.0/.NET2.0/Visual Studio2005対応」
 「11.4 例外の再スロー」で、
 理由の説明はないが、「Throw」(引数なし)の記述を推奨しています。
[検証]  Throwステートメントの仕様について、  2つのパターンの検証プログラムを実行して、結果を比較してみた。
パターン1
 Throwステートメントに引数がある場合、
 Button1_Click()→Method1()→Method2()→Method3()→Method4()と
 メソッドを呼び、最下層のMethod4()で
 ゼロ除算の例外(System.DevideByZeroException)を意図的に発生させる。
 各々のメソッドのCatch句では、「Throw ex」(引数あり)で例外を再Throwする。
 最上位メソッドにて、例外の内容(ToString()した結果)を表示する。
パターン2
 Throwステートメントに引数がない場合、
 Button1_Click()→Method1()→Method2()→Method3()→Method4()と
 メソッドを呼び、最下層のMethod4()で
 ゼロ除算の例外(System.DevideByZeroException)を意図的に発生させる。
 各々のメソッドのCatch句では、「Throw 」(引数なし)で例外を再Throwする。
 最上位メソッドにて、例外の内容(ToString()した結果)を表示する。
・パターン1(Throwステートメントに引数がある場合)
Private Sub Button1_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
    Dim num As Integer

    Try
        num = Method1(1, 0)
    Catch ex As Exception
        MessageBox.Show( _
            ex.ToString(), _
            "例外[Throwの引数がある場合]")
    End Try
End Sub

Private Function Method1( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        Return Method2(a, b)
    Catch ex As Exception
        Throw ex
    End Try
End Function

Private Function Method2( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        Return Method3(a, b)
    Catch ex As Exception
        Throw ex
    End Try
End Function

Private Function Method3( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        Return Method4(a, b)
    Catch ex As Exception
        Throw ex
    End Try
End Function

Private Function Method4( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        'b=0の時に例外が発生する。
        Return a \ b
    Catch ex As Exception
        Throw ex
    End Try
End Function
[VB.NET]例外[Throwの引数がある場合]
・パターン1の実行結果(Throwステートメント[引数あり])  Throwの引数を指定した場合は、  保持されているトレース情報は、Method1、Button1_Clickのみで、  Method2、Method3、Method4は保持されていないことがわかります。  引数指定での再スローでは、それより前のトレース情報を保持しない為です。  この例では、   Method3で再スローする時に、Method4以降のトレース情報を保持しない、   Method2で再スローする時に、Method3以降のトレース情報を保持しない、   Method1で再スローする時に、Method2以降のトレース情報を保持しない為、  図のような結果になります。 例外[Throwの引数がある場合] ・パターン2(Throwステートメント[引数なし])
Private Sub Button1_Click( _
    ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles Button1.Click
    Dim num As Integer

    Try
        num = Method1(1, 0)
    Catch ex As Exception
        MessageBox.Show( _
            ex.ToString(), _
            "例外[Throwの引数がない場合]")
    End Try
End Sub

Private Function Method1( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        Return Method2(a, b)
    Catch ex As Exception
        Throw
    End Try
End Function

Private Function Method2( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        Return Method3(a, b)
    Catch ex As Exception
        Throw
    End Try
End Function

Private Function Method3( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        Return Method4(a, b)
    Catch ex As Exception
        Throw
    End Try
End Function

Private Function Method4( _
    ByVal a As Integer, _
    ByVal b As IntegerAs Integer

    Try
        'b=0の時に例外が発生する。
        Return a \ b
    Catch ex As Exception
        Throw
    End Try
End Function
[VB.NET]例外[Throwの引数がない場合]
・パターン2の実行結果(Throwステートメント[引数なし])  Throwの引数を指定しない場合は、  関連するトレース情報(Method1、Method2、Method3、Method4、Button1_Click)を  全て保持していることがわかります。 例外[Throwの引数がない場合]

0 件のコメント:

コメントを投稿

[雑記]ドローン(DJI Mini 3)

(1)雑記 もともと多趣味の友人 masakazu Drone 氏が、 最近、 ドローン にハマり始めて、 更に、新たな趣味が増えたとのこと。 ドローン を始めてから、 まだ1年も経っていないとのことですが、 旅行先で山や川の景色を 空撮 して、 Youtube ...