2004年01月04日
川俣晶の縁側ソフトウェア技術雑記 total 8042 count

C#において、メソッド等のデフォルトがvirtualではないことのデメリット

Written By: 川俣 晶連絡先

 Javaのメソッドは、何も指定しない場合、virtual扱いです。つまり、継承した際に、サブクラス側でメソッドを入れ替えることができます。これに対して、C#のメソッドは、何も指定しない場合、virualではないと扱われます。継承した際に、メソッドを入れ替えることを意図する場合は、明示的にvirtualであることを示さねばなりません。

 Java信者はこの点でC#は効率のために理想を捨てた、というような批判を行いますが、これは当たっていないと思います。virtualな仮想メソッドは、プログラムの動作の見通しを悪くするので、本当にそれが必要な箇所以外では使わない方が、結果としてシンプルで分かりやすいソースになると思います。そういう意味で、明示的にvirtualと指定しなければ仮想メソッドにならないC#の方が、よりあるべき姿に近いものであるように思います。(思うだけで、それが本当にそうであるかどうかは、もっと使い込んで比較しないと分かりませんが)

しかし、それでは困るケースというのも…… §

 実は、何も指定しない場合virual扱いではない、ということが具体的な問題になるケースに遭遇しました。

 あるクラスの単体テストを書いているときに、あるオブジェクトに設定した値が正しいかどうかテストできないことに気付きました。値を書き込むメソッドはあっても、取り出すメソッドがありません。通信絡みなので、もし送ってしまったデータであれば、取り出せないこともあり得るでしょう。その点で、仕様が不当であるとは言えません。

 テストのために仕様変更するという手もありますが、クラスライブラリの中のクラスなので、書き換えはできません。

 ここはmockオブジェクト(mock object)を使うべきケースにあたるのかな、と思って少し調べてみたところ。

 Javaの場合は、テスト対象のクラスを継承し、ダミー値を返すメソッドでオーバーライドすることができるわけですね。こういう方法で作ったmockオブジェクトは、本来のオブジェクトを受け渡す文脈で使うことができるので、テストが可能になります。

 しかし、これが可能となるのは、Javaのメソッドは何も指定しない場合virtual扱いになっているためです。オーバーライドされることを具体的に意図しないメソッドであってもvirtual扱いとなっているケースが多いので、というより、特に理由がない限りオーバーライド可能にしておき、最大限の拡張性を確保することがJavaの思想性であるために、たいていのメソッドをオーバーライドできます。しかし、C#では、オーバーライドされることを意図していないメソッドにvirtualキーワードを付ける慣習はなく(拡張性というパワーを与えすぎることはトラブルの元だ!)、このような方法でmockオブジェクトを作ることができません。

 C#でmockオブジェクトをどうするか、ひいては同じような特徴を持つVisual Basic .NETなどでどうするか、1つの課題ということになるかもしれません。

 もちろん、これはJavaの方が優れているという話でもなく、何も指定しない場合virtual扱いにすれば済む話でもありません。念のため。

2004年1月5日11時35分頃追記 §

 この問題の解決編をmockオブジェクトを作成できない場合に、delegateを使ってテスト可能なクラスを設計する方法として書きました。