2009-10-05 6 views
5

私は何かのさまざまなサブクラスと、それらのサブクラスのインスタンスで動作するアルゴリズムを持っていて、アルゴリズムの動作がインスタンスのサブクラスの種類によって多少異なる場合、これを行うための最も一般的なオブジェクト指向の方法です仮想メソッドを使用しています。実行時の型情報はいつ使用しますか?

たとえば、サブクラスがDOMノードの場合、アルゴリズムが子ノードを挿入する場合、そのアルゴリズムは親ノードがDOM要素(子を持つことができる)かDOMテキストt):insertChildrenメソッドは、DomNode基本クラスの仮想(抽象)であり、DomElementおよびDomTextサブクラスのそれぞれで異なって実装されます。

もう1つの可能性は、値を読み取ることができる共通のプロパティをインスタンスに与えることです。たとえば、アルゴリズムはDomNode基本クラスのnodeTypeプロパティを読み取ることがあります。別の例では、共通のパケットヘッダーを共有するネットワークパケットの種類(サブクラス)が異なる場合があり、パケットヘッダーを読み取ってそのパケットの種類を確認できます。

私には、多くの実行時型情報を使用していない:ネットドットで

  • Object.GetType方法をダウンキャストC#の
    • isasキーワード
    • C++のtypeid演算子

    新しいアルゴリズムを追加するときmはサブクラスのタイプに依存しますが、クラス階層に新しい仮想メソッドを追加する傾向があります。

    私の質問は、仮想関数の代わりに実行時型情報を使用するのが適切なのはいつですか?

  • 答えて

    5

    他の方法はありません。仮想メソッドは常に優先されますが、使用できないことがあります。いくつかの理由がありますが、最も一般的なのは、あなたが作業したいクラスのソースコードを持っていないか、変更できないということです。これは、レガシーシステムやクローズドソースの商用ライブラリで作業する場合によく発生します。

    .NETでは、プラグインのように新しいアセンブリを即座に読み込む必要があり、基本クラスがなく、ダックタイピングのようなものを使用する必要があります。

    +0

    RTTIが非難された理由は何ですか?最後の手段ですか? – ChrisW

    +2

    @ChrisW、それは理解するのが難しく、実行するのがずっと遅いです。 – vava

    +0

    RTTを仮想関数ポインタと同じようにvtableクラスに格納することができます。 RTTIをチェックする方法がよりローカルになっているので、なぜ理解が難しいのか分かりません。たとえば、 "if(foo is Foo)"と表示された場合、定義を調べたり調べたりすることなく、いくつかのサブクラスで仮想関数の – ChrisW

    3

    C++では、(ほとんどデザインの選択肢が劣る)不明瞭なケースの中で、RTTIはいわゆるmulti methodsを実装する方法です。

    +0

    これは「はい」です。別の方法は、仮想関数のみを使用するhttp://en.wikipedia.org/wiki/Double_dispatchを使用することです。パラメータ型の各サブクラスに新しい仮想関数を追加します。 – ChrisW

    +0

    投稿したリンクを見ると、実際にはダブルディスパッチの一般化である「マルチディスパッチ」につながることがわかります。 ':)' – sbi

    0

    dynamic_cast <>私が正しく覚えていれば、RTTIに依存しています。いくつかのあいまいな外部インターフェイスは、オブジェクトがvoidポインター(何らかの理由でが発生する可能性があります)が渡されたときにRTTIに依存することもあります。

    言われているように、私は10年間のプロC++のメンテナンス作業で野生でtypeof()を見たことがありません。 (幸運にも。)

    +1

    typeofはdecltypeキーワードとしてC++ 0xで追加されます(非常に便利です、IMO)。実行時の型情報ではなく、コンパイル時の構造であり、C++コンパイラが持っている情報の一部を生産的に使用する方法ですが、現在のところ、エラーメッセージだけを使用します。 – UncleBens

    0

    ランタイムタイプのチェックがOKの場合は、More Effective C#を参照してください。

    項目3。特殊アルゴリズムジェネリックアルゴリズム 実行時型チェックの使用

    でジェネリックを簡単に再利用することができます。 新しいタイプ の新しいインスタンス化は、同様の機能を持つ を持つ新しいタイプを意味します。

    コードが少ないので、これはすべて素晴らしいです。しかし、ときどき より一般的な意味は、具体的に の利点を取っていないが、明らかに優れたアルゴリズムである を明らかにしている。これにはC# の言語規則が適用されます。あなたが を認識するため すべてそれが取るに​​は、より大きな能力を持っている、と 型パラメータは、その後 に特定のコードことを書くとき、あなたのアルゴリズムは より効率的にすることができるということです。さらに、 が異なる制約 を指定する第2ジェネリック型を作成することが常に機能するとは限りません。一般的な のインスタンス化は、オブジェクトのコンパイル時タイプ と実行時タイプではない に基づいています。 に失敗した場合は、 の可能性があります。

    たとえば、IEnumerable < T>で表されるアイテムのシーケンスに逆順列挙を提供するクラスを作成するとします。逆方向に列挙するには、リスト< T>のようなインデクサアクセスを持つ中間コレクションに項目をコピーして、インデクサアクセスを逆に使ってコレクションを列挙することができます。しかし、元のIEnumerableがIListの場合、それを利用して、より効果的な方法(中間のコレクションにコピーしないで)を提供して、アイテムを逆向きに反復するのはなぜでしょうか。だから、基本的には、我々が利用できる特殊なものですが、同じ動作(シーケンスを逆順に繰り返します)を提供します。

    一般的に、実行時の型チェックを慎重に検討し、Liskov Substituion Principleに違反していないことを確認する必要があります。

    1

    イベントハンドラは通常、オブジェクトを共通の祖先にダウンキャストしているため、この構造体(「is」および「as」)はDelphi開発者にとって非常によく知られています。たとえば、イベントOnClickは、TButton、TListBox、またはその他のいずれであっても、オブジェクトのタイプに関係なく、唯一の引数Sender:TObjectを渡します。このオブジェクトに関する詳細を知りたければ、 "as"でアクセスする必要がありますが、例外を避けるために、以前は "is"でチェックすることができます。このダウンキャスティングにより、厳密なクラス型チェックでは不可能であった、オブジェクトとメソッドのデザイン型バインディングが可能になります。ユーザーがButtonやListBoxをクリックしても同じことをしたいと考えていますが、異なるプロトタイプの関数を提供している場合、同じプロシージャにバインドすることはできません。

    より一般的な場合、オブジェクトは、たとえばオブジェクトが変更されたことを通知する関数を呼び出すことができます。しかし、それはあらかじめ、目的地を "個人的に"知っている可能性を残しています。これは、すべてのオブジェクトの最も一般的な祖先として自己を渡すことによって行います(Delphiの場合のTObject)

    +0

    はい、イベントハンドラは良い例です。より一般的には、あなたのアプリケーションがあなたのアプリケーションタイプを知らないフレームワークコードを使ってアプリケーションタイプへのポインタを格納したいときはいつでも、例えば、ドットネットフレームワーク型の多くの 'object Tag'プロパティとWin32' CreateThread'関数に渡される 'void * lpParameter'を使用します。あなたはそれを簡単に十分に保存することができますが、それを取り戻すときにはダウンキャストする必要があります。 – ChrisW

    +0

    @ChrisW:私はあなたが 'void *'(少なくともC++ではない)から 'dynamic_cast'できるとは思いません。コンパイラは、指し示されたアドレスに多態型のオブジェクトがあり、その内部データのレイアウトは何かを知る方法は? – sbi

    関連する問題