- PR -

基底クラスで発生した例外のスタックトレースに派生クラスの名前を出したい

1
投稿者投稿内容
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-04-19 12:37
ぼのぼのです。いつも大変参考にさせて頂いております。

今、VB.NETでクラスライブラリを作ってます。

似たような処理がいくつもあるので、継承を使って共通的な処理を基底クラスに、固有の処理を派生クラスに実装しています。具体的に以下のようになっています。
(メンバ変数や細かい実装は省略。あと実際には名前に全角使ってません。)

コード:
Public MustInherit Class 親

    Public Sub New()
    End Sub

    Public Sub 処理()
        固有処理()
        共通処理()
    End Sub

    Protected MustOverride Sub 固有処理()

    Private Sub 共通処理()
        '@
    End Sub

End Class

Public Class 子1
    Inherits 親

    Protected Overrides Sub 固有処理()
        'A
    End Sub

End Class

Public Class 子2
    Inherits 親

    Protected Overrides Sub 固有処理()
        'B
    End Sub

End Class

'メインルーチン
Try
    Dim ins1 As New 子1
    Dim ins2 As New 子2
    ins1.処理()   'C
    ins2.処理()   'D
Catch ex As Exception
    'ログにMessageとStackTraceを記録
End Try


これでうまく動いているんですが、一つ問題がありまして
それは、@のとこで例外が発生すると、スタックトレースに基底クラスの名前しか出てこないので、CとDどっちで起こったのかが特定できない、ということです。

そこで、親クラスの処理()にOverridableをつけて、各派生クラスに基底クラスの処理()をコールするだけの処理()を加えたところ、スタックトレースに派生クラスの名前も出るようになりました。

しかしこれでも問題が残ります。派生クラスに処理()を書き忘れてもコンパイルを通ってしまい、処理()を書き忘れたとこでは、当然ながらスタックトレースに基底クラスの名前しか出てきません。

そこで今度は基底クラスの処理()をProtectedにして、派生クラスにPublic Shadowsで同様の処理()を実装するようにしたところ、これを書き忘れるとCDのとこでコンパイルが通らず、
スタックトレースに派生クラスの名前も出るようになり、一応目的は達成できました。

しかし、もっとスマートな方法はないものかと、ちょっと釈然としないとこが残っています。
目的は「スタックトレースをわかりやすくすること」なので、子の方に何も書かなくて済む方法があればベストなんですが、さすがにそれは無理?(^^;
時間があればデザインパターンをちゃんと勉強すべきなんでしょうが…
識者のご意見を伺ってみたいです。よろしくお願いします。m(_ _)m
Ten.
ベテラン
会議室デビュー日: 2003/04/03
投稿数: 67
投稿日時: 2005-04-20 12:03
こんにちは

呼び出し元のソースを見れば分かるので、単に実行時の型が知りたいだけならば変に凝った実装にしないほうが良いのではないでしょうか?

それでも必要な場合は、「共通処理」で例外が発生した際に実行時の型の情報を含んだ独自の例外をスローする、というのはどうでしょう。(実際の例外は内部例外に持つ)
todo
ぬし
会議室デビュー日: 2003/07/23
投稿数: 682
投稿日時: 2005-04-20 12:46
引用:

目的は「スタックトレースをわかりやすくすること」なので、



標準のスタックトレースが不満であれば、自前で作るとか。

StackFrameクラス?
http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=17511&forum=7

ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-04-20 15:33
ぼのぼのです。

>呼び出し元のソースを見れば分かるので

確かにソースを追えばわかるんですが、リリースビルドにしてしまうとスタックトレースに行番号が出なくなるので、スタックトレースだけから即座にCDどっちで発生したかを判断することはできなくなってしまいます。

>標準のスタックトレースが不満であれば、自前で作るとか

実は「メインルーチン」のとこは私が作るんじゃないんです。ここでは、ex.GetType.ToStringとex.Messageとex.StackTraceの3つだけを出力することが既に決まっています。
なので、スタックトレースを自前で作るとしても、独自の例外を定義してStackTraceプロパティをオーバーライドすることになります。

>実行時の型の情報を含んだ独自の例外をスローする

で、これをちょっと試してみたんですが、「実行時の型の情報」って@のブロック内で取得可能なんでしょうか?もしご存知でしたらご教授ください。

ちなみに、@のとこで
 Dim sf = New StackFrame(0)
 Dim rt As String = sf.GetMethod.ReflectedType.ToString
ていうのは試してみたんですが、「親」になってしまいました
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-04-20 16:03
引用:

ぼのぼのさんの書き込み (2005-04-20 15:33) より:

で、これをちょっと試してみたんですが、「実行時の型の情報」って@のブロック内で取得可能なんでしょうか?もしご存知でしたらご教授ください。


普通にthis.GetType()で可能です。

VB.NETだとthisじゃなくてMeでしたっけ。

[ メッセージ編集済み 編集者: Hongliang 編集日時 2005-04-20 16:19 ]
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-04-20 16:57
ほんとだ
難しく考えすぎてました。

あとは実装次第ってことですね。どーしよっかなぁ…
後学のために思考の過程を書いておきます。

方法1
親の処理()をPublic Overridableにして、各子クラスにMyBase.処理()とだけ書いた処理()を実装
<利点>
・自前の例外を書かなくてすむ
<欠点>
・各子クラスに処理()を書かなくてはならない。
・子クラスに処理()を書き忘れてもコンパイルを通る。
⇒ソースをちゃんとチェックすればいいだけの話。考えようによっちゃ利点とも言える。

方法2
親の処理()をProtectedにして、各子クラスにMyBase.処理()とだけ書いたPublic Shadows 処理()を実装
<利点>
・自前の例外を書かなくてすむ
・子クラスに処理()を書き忘れるとコンパイルを通らない。
<欠点>
・各子クラスに処理()を書かなくてはならない。
・目的を知らない人がソースを見たら「なんでこんなことしてるんだ?」と思う。
⇒目的をコメントに書いとく。それでも「変に凝った実装」であることは確か。

方法3
親の処理()内で例外が発生したら一度Catchして、発生した例外と実行時の型情報を含んだ自前の例外をThrow
<利点>
子クラス側に何も書かなくて良い。
<欠点>
・自前の例外を書かなくてはならない。
・StackTraceを書き換えてしまうのは本質的に間違ってると思うので、必要な情報は全てMessageに出すとして、出す必要があるのは実行時の型情報と実際に発生した例外の型情報とMessage。処理()からさらにサブルーチンを使ってればStackTraceも必要かな?結果的にMessageは結構な長さになる。
⇒例外が発生した時のみ記録されるものだし、そもそも例外が発生しないように作るべきだが、それにしても如何なものか。

こうやって整理してみると、方法2はやっぱりなんか不自然な気がします。
もうちょっと迷ってから方法1と方法3のどっちかでいってみたいと思います。
回答下さったみなさん、ありがとうございました。
1

スキルアップ/キャリアアップ(JOB@IT)