« 東方アクションゲームねた | メイン | Managed DirectX の資料求む »

2006年06月27日

C#のポリモーフィズム [C#]

最近書いているC#のコードがどうにも変な動作をすると思ったら、 どうやらC#は継承の概念がJavaと若干違うらしい。

public class TestClass{
  // エントリポイント
  public static void Main(){
    // クラスB のインスタンスを作成
    ClassB hoge = new ClassB();
    // クラスA として Message メソッド起動
    ((ClassA)hoge).Message();
  }
}

// スーパークラス
public class ClassA{
  // 上書きされるメソッド
  public void Message(){
    Console.WriteLine("俺はクラス A である。");
  }
}

// ClassA を継承したサブクラス
public class B : ClassA{
  // Message メソッドを上書き
  public new void Message(){
    Console.WriteLine("私はクラス B です。");
  }
}

上記は基本的な継承を利用したサンプルコードである。 なお Java と C# では文法が限りなく似ているので、Javaのコードは方は割愛した。 出力結果は以下のとおりだ。

Java の場合
 私はクラス B です。 
C# の場合
 俺はクラス A である。 

このように、全く違った結果が返ってくる。 つまり、Java ではそのクラス自体のメソッドを優先的に利用するのに対し、 C# では呼び出して時点でどのクラスとして認識されているかによってどのメソッドを呼び出すか決めるらしい。

どっちがいいかと聞かれたら、この場合は圧倒的に Java の方を支持する。 一体何を考えてC#はこんな実装をするようになったんだろう。 これじゃ、何のためのポリモーフィズムなんだかわからない。 C++の頃からそうなんだろうか。不思議だ。

具体的にどんな時に困るかというと、GoF の Composite パターンを利用するときなどに不具合が出てくる。 子ノードの実体が何であろうとその時点での型のメソッドを呼び出してしまうので、混合させる意味が無くなってしまうのだ。 これじゃ全然使い物にならない。

スーパークラスを virtual にして、new ではなく override にして実装すれば確かにそのような事は起こらない。しかし virtual なクラスはそれ自体では使うことができない。つまりクラスの再利用という形は取ることが出来ないわけだ。

より結びつきの薄いインタフェースを使えということなのだろうか。 まあ確かに継承の多用は頂けないというのは理解できるが、使わなくちゃならないときだってある。 何かの勘違いか、あるいはもっと良い方法があれば良いのだが・・・

投稿者 : 11:53 | コメント (4) | トラックバック (0)

トラックバック

このエントリーのトラックバックURL:
http://totora.jpn.org/mt/mt-tb.cgi/150

コメント

私はC#は全く知らないので完全な推測になってしまいますが、

((ClassA)hoge).Message();

この行でhogeをClassAにキャスト(?)をしているのが原因ではないでしょうか。キャストの意味がCと同じなら、ここでhogeをClassAの一時オブジェクトにコピーすることになり、動的な型がClassAになってしまったためClassAのメソッドが呼ばれる、と。C++ではそうなるはずです。Javaのほうも私はやっていないのですが、多分Javaは元から全て参照だから問題ない…のでしょうかね。よくわかりませんが。無責任な突っ込みで済みませぬ。

投稿者 空岬 : 2006年07月09日 23:15

なるほど。クラスAとしてコピーされるのですか。
C++のオブジェクト指向にはわずかしか触れたことが無いので、どうにもわからないことが時々あります。

投稿者 無重力 : 2006年07月10日 14:04

済みません、その後ちょっとこの辺
http://ufcpp.net/study/csharp/oo_polimorphism.html
で調べてみたのですが、キャストは関係なかったみたいです。適当な事を言ってしまって申し訳ない。
実際にはC#ではオーバーライド先の関数にはoverride修飾子をつける必要があるということらしいです。
いい加減な知識で発言するもんじゃないですね。本当済みません。ちょっとダイヤモンド継承で悶絶してきます。

投稿者 空岬 : 2006年07月10日 22:11

ふむふむ。
継承元を virtual にして override すれば、キャストしても普通に使えるんですね。
どうやら abstract メソッドとごっちゃになっていたようです。
てっきり virtual なメソッドを持つクラスはインスタンス化できないと思っていたので。

わざわざ調べていただきありがとうございました。
これで疑問が解決しました。

投稿者 無重力 : 2006年07月12日 12:55

コメントしてください




保存しますか?

(書式を変更するような一部のHTMLタグを使うことができます)