none
基底クラスを指定して、そのサブクラス群のインスタンスを生成したい RRS feed

  • 質問

  • いつもお世話になっております。
    C#2010で開発しています。

    基底クラスを指定して、そのサブクラス群のインスタンスを生成したいと思っております。

    次のようなクラス群であれば、「Base」クラスを指定して、
    Sub1_1、Sub1_2、Sub2_1、Sub2_2クラスの
    インスタンスを生成したいということです。

    Sub1やSub2、Baseのインスタンスが同時に生成されても構いません。

    サブクラスがかなりたくさんあり、1つずつ指定するのが、
    少し大変でしたので、何かスマートな方法があれば教えて頂けないかと思い、
    質問させて頂きました。

    ぜひ、よろしくお願い致します。


    2011年8月20日 3:05

回答

  • なぜそんなことをしたいのかがよくわかりませんでした。

    たとえば、Sub2_1 型が自動的に生成されたとして、それが Sub2_1 型だとわからなくても(Base 型変数に見えても)かまわないのでしょうか?
    正直、Base 型の継承ツリーの末端クラスをまとめて作ったとして、どのように利用するのかがわからないので、何ともしようがありませんね。

    # ファクトリメソッドで new Sub2_1(); new Sub2_2(); とか一カ所にまとめて書けばいいんじゃないのとかそういうイメージ。
    # または、リフレクションで探しまくって、Base 型の配列・リストにするイメージ。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク コンドル 2011年8月20日 4:38
    2011年8月20日 3:51
    モデレータ
  • 外池と申します。あくまで、アイデアです。検証はしていません。

    C#の言語仕様としては、一般論として、お望みのことは無理かと思います。これは、ご理解されていますよね? 言語の仕組みからして、クラスの派生はプログラミング時の操作であって、インスタンスを作成する実行時の操作ではないからです。

    お望みのことは実行時に行う操作なので、実行時にクラスの情報を取り扱うための.Net Frameworkの仕組みを使うことになろうかと思います。Reflection名前空間のAssembly.GetTypesを使えば、とりあえず「全部のクラス」がTypeの配列として得られるので、個々のTypeのBaseTypeプロパティーを見るか、IsSublcassOfメソッドを呼び出すかで、ある基底クラスから派生したものかどうか判断します。そうやって選び出した派生クラスのTypeのFullNameに基づいて、Assembly.CreateInstanceを使ってインスタンス化する・・・。

    こんな感じではないでしょうか?

    いずれにせよ、基底クラス側から派生クラスを探すのではなく、とにかくクラス全部をリストアップして、そこからお望みの基底クラスから派生したものを探す、というイメージです。


    (ホームページを再開しました)
    • 回答としてマーク コンドル 2011年8月20日 4:38
    2011年8月20日 4:00

すべての返信

  • なぜそんなことをしたいのかがよくわかりませんでした。

    たとえば、Sub2_1 型が自動的に生成されたとして、それが Sub2_1 型だとわからなくても(Base 型変数に見えても)かまわないのでしょうか?
    正直、Base 型の継承ツリーの末端クラスをまとめて作ったとして、どのように利用するのかがわからないので、何ともしようがありませんね。

    # ファクトリメソッドで new Sub2_1(); new Sub2_2(); とか一カ所にまとめて書けばいいんじゃないのとかそういうイメージ。
    # または、リフレクションで探しまくって、Base 型の配列・リストにするイメージ。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク コンドル 2011年8月20日 4:38
    2011年8月20日 3:51
    モデレータ
  • 外池と申します。あくまで、アイデアです。検証はしていません。

    C#の言語仕様としては、一般論として、お望みのことは無理かと思います。これは、ご理解されていますよね? 言語の仕組みからして、クラスの派生はプログラミング時の操作であって、インスタンスを作成する実行時の操作ではないからです。

    お望みのことは実行時に行う操作なので、実行時にクラスの情報を取り扱うための.Net Frameworkの仕組みを使うことになろうかと思います。Reflection名前空間のAssembly.GetTypesを使えば、とりあえず「全部のクラス」がTypeの配列として得られるので、個々のTypeのBaseTypeプロパティーを見るか、IsSublcassOfメソッドを呼び出すかで、ある基底クラスから派生したものかどうか判断します。そうやって選び出した派生クラスのTypeのFullNameに基づいて、Assembly.CreateInstanceを使ってインスタンス化する・・・。

    こんな感じではないでしょうか?

    いずれにせよ、基底クラス側から派生クラスを探すのではなく、とにかくクラス全部をリストアップして、そこからお望みの基底クラスから派生したものを探す、というイメージです。


    (ホームページを再開しました)
    • 回答としてマーク コンドル 2011年8月20日 4:38
    2011年8月20日 4:00
  • Azulean様、外池様

    ご回答ありがとうございます。

    外池様のアドバイスを受け、次のように実現できそうです。

                Assembly a = Assembly.GetEntryAssembly();
                foreach (Type t in a.GetTypes())
                {
                    if (t.IsSubclassOf(new Control().GetType()))
                    {
                        a.CreateInstance(t.Name);
                    }
                }

     

    やりたかったことは、複数のクラスに対するインスタンスを一度に作成して、
    それをマルチスレッドで一度に実行したいというものでした。

    サブクラスが300以上あるため、1つずつ書いても良いのですが、
    クラスが更に増えた場合に、また書かなければならないのであれば、
    保守性に欠けると思い、ご質問させていただきました。

    ご返信いただき、ありがとうございます。
    大変助かりました。

    2011年8月20日 4:38
  • 外池です。例示されているプログラムを見て、ちょっと心配になりました。

    お尋ねの件自体は解決できると思うのですが、300以上あるサブクラスは、Controlから派生したものなんでしょうか? Controlを異なるスレッドで動かすことは、まったく別の問題を引き起こしますので、念のため・・・。あと、300ものControlのサブクラスを一度にインスタンス化して、それぞれ異なるスレッドに割り付けるというのも、かなり大胆なことかと・・・。

    Azuleanさんも仰っているように、「何故?」と、気になりますが。


    (ホームページを再開しました)
    2011年8月20日 11:12
  • if (t.IsSubclassOf(new Control().GetType()))

    そもそも、この一行無駄がありすぎです。
    単に Type 型ほしいなら、typeof で十分でしょう。

    外池さんも指摘されていますが、コントロール派生クラスとマルチスレッドの関係が全く想像つきません。
    画面の生成に時間がかかるからバックグラウンドでやりたいという狙いがあったとしても、それは実現できませんよ。

    私見ですが、(同一層で)まとめて生成したいクラスが 300 以上ある時点で、保守性はすでに低いと思います。
    リフレクションでまとめて書けたところで、さらに悪化するだけです。(生成したくないものまで生成してしまうリスクがあるし、そもそもリフレクションは可読性が悪いし、関連性も見えにくくなる)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年8月20日 13:48
    モデレータ
  • ご回答ありがとうございます。

    サンプルのコードがよくなかったです。
    簡単に検証しようと思い、Controlを使用しましたが、
    私がクラス群を例示しているのですから「Base」クラスにしなければなりませんでした。
    申し訳ございません。

    行いたい処理を具体的に記載しますと、
    ビジネスロジックを処理するためのクラスを生成したいと思っております。

    10個程度のデータベースから、一度に値を取得したいという要望があり、
    取得したいデータの種類から考えて、合計300程度のクラスを作成しました。
    それぞれのクラスで行うデータ取得を、マルチスレッドで行いたいと考えております。

    オリジナルの基底クラスを用意しておりまして、
    今回インスタンスを生成したいクラスは、すべてのその基底クラスを継承しています。
    これであれば、Controlと記載した部分を、今回作成したオリジナルの基底クラスに
    差し替えれば使えるだろうと考えて、十分対応できそうだと判断した次第でございます。

    分かりにくい説明をしてしまい、申し訳ございません。
    また、typeofの使い方も勉強になりました。

    ありがとうございます。

    2011年8月20日 15:29
  • 誤:Assembly a = Assembly.GetEntryAssembly();
    正:Assembly a = Assembly.GetExecutingAssembly();

    ビジネスロジックを別プロジェクトにしていますので、「誤」のほうではだめですね。訂正いたします。

    2011年8月20日 15:44
  • 「10個程度のデータベース」はそれぞれテーブル構造が異なるのでしょうか? 例えば、テーブル構造は同一で接続先のみ異なるのなら、それぞれに対してクラスを作成する必要はありません。接続先プロパティを持たせ、呼び出し側が接続先プロパティを適切に初期化すれば1つのクラスで済みます。

    これは一例であって実際にはどのような構造になっているかわかりませんが、それぞれのクラスが単調なコードコピーで実現されているのなら、クラス設計そのものを見直すべきです。クラスが300個並ぶのは常識的にはあり得ません。

    2011年8月20日 21:09
  • 本筋についてはすでに指摘している・指摘されているのでちょっとおまけ的なところを。

    MSDN/TechNet フォーラムに先日から画像貼り付け機能がついています。
    投稿フォームの一番右端にそのボタンがありますので、今後の画像貼り付けにはそれを使ってみてください。(消えてしまっているようなので)

     


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年8月20日 23:57
    モデレータ
  • ご回答ありがとうございます。

    処理はそれぞれ異なっております。
    単調なコードコピーでは実現されていません。

    基本的には同じ枠組みで作りたいという思いがあったため、
    継承させているのです。
    インターフェースでも良いかもしれませんが、共通の処理もありますので、
    継承させています。

    ただし、一度に300回以上SQLを投げている、
    そのようなプログラムを作らなければならなくなった、
    その設計方法には、問題があったかもしれません。

    再検討してみたいと思います。

    ※画像は貼り付け機能を利用しました。
    貼り付け機能があったことを知りませんでした。
    ありがとうございます。

    2011年8月22日 14:47