none
型を変数に格納し動的にジェネリックの型指定をしたい RRS feed

  • 質問

  • お世話になってます。
    早速質問させて下さい。

    動的に型を調べ、その型でジェネリックの型指定をしたいのですがどのようにすればよいのでしょうか?

    以下のようにしてもコンパイルエラーが出てしまいます。
    MyClass cls;
    List<cls.GetType()> lst = new List<cls.GetType()>(); //コンパイル出来ない

    イメージとしてはDelphiのクラス参照型のようなものなのですが実現出来ますでしょうか?
    2009年10月29日 8:17

回答

  • 作れないことも無いけどIListとして扱うぐらいしか出来ませんよ

    class Program
    {
        static void Main(string[] args)
        {
            //generic型パラメータの型
            System.Type parameterType  = typeof(DummyClass);
    
            // List<Dummy>のTypeを作成
            System.Type genericBaseType = typeof(System.Collections.Generic.List<>);
            Type genericType = genericBaseType.MakeGenericType(parameterType);
    
            // List<DummyClass>のインスタンス作成
            IList list = (IList)System.Activator.CreateInstance(genericType);
    
            // 以下テスト
            Type listType = list.GetType();
            Console.WriteLine("ジェネリックになってる? " + listType.IsGenericType);
            Console.WriteLine("*** ジェネリックのパラメータを列挙 ***");
            foreach (Type gen in listType.GetGenericArguments())
            {
                Console.WriteLine(gen.ToString());
            }
            Console.WriteLine("*** ジェネリックのパラメータを列挙 ***");
    
            try
            {
                list.Add(new DummyClass());
                Console.WriteLine("OK : DummyClass2は入るよ");
            }
            catch
            {
                Console.WriteLine("NG : DummyClass2が入らなかったよ");
            }
            try
            {
                list.Add(new DummyClass3());
                Console.WriteLine("NG : DummyClass3が入っちゃったよ");
            }
            catch
            {
                Console.WriteLine("OK : DummyClass3は入らないよ");
            }
            try
            {
                list.Add(new object());
                Console.WriteLine("NG : objectが入っちゃったよ");
            }
            catch
            {
                Console.WriteLine("OK : objectは入らないよ");
            }
    
            Console.WriteLine("型は同じになるの? : " + (typeof(DummyClassList) == genericType).ToString());
            Console.WriteLine("型は同じになるの? : " + (typeof(List<DummyClass>) == genericType).ToString());
        }
    
        internal class DummyClass { }
        internal class DummyClass2 : DummyClass { }
        internal class DummyClass3 { }
        internal class DummyClassList : List<DummyClass> { }
    }
    • 回答としてマーク __test__ 2009年10月29日 10:46
    2009年10月29日 9:23

すべての返信

  • 根本的な質問ですが、
    ArrayListを使用する形では実現出来ないのでしょうか。

    2009年10月29日 8:41
  • ジェネリックは型セーフにすることによって、コンパイル時に安全を得るためのものなので、そういう用途では無意味じゃないですかね。
    aviator__さんがおっしゃるように、ArrayListでいいんじゃないでしょうか。
    2009年10月29日 9:00
  • 返信ありがとうございます。
    少し長くなるのですが思いついた経緯は以下のようになります。

    自分自身をデシリアライズするクラスを作るには 購読している

    上記質問の回答でジェネリックを使用したFactoryパターンを教えて頂きました。
    そこで以下のように作って見ました。

    public class MyFactory<T> where T : MyClass {
        public T CreateInstance() {
          MyClass result = null;
          if (typeof(T) == typeof(MyChildClassA)) {
            result = new MyChildClassA();
          } else if {....}
          return (T)result;
        }
    }

    Tを引数とし、その型情報をみてインスタンスを作るという構造です。

    使用する側はこのようになります。
    MyClassRef ref = MyChildClass; //このように型を変数に格納したい(MyClassRefはクラス参照型と仮定)
    MyClass cls = MyFactory<ref>.CreateInstance();
    しかし参照型がないため問題が出てきた次第です。

    素直に新しくMyFactoryを作りMyFactory.CreateInstance(int param);とでもしてparamの値を見ながら何を作るのか判定してもよいのですが
    それだとMyFactory<T>とMyFactoryの二つを作る事になってしまいます。(前回の質問内容も実装したい為)
    かといってMyFactory<T>.CreateInstance(int param);としても<T>の部分は判定として使われず意味がないので美しくありません。

    参照型を使用しない方法を模索したのですが今ひとつ良い案が浮かびませんでした。
    2009年10月29日 9:04
  • なにか引っかかるのですが・・・

    先の 自分自身をデシリアライズするクラスを作るには で提示したコードは、

    「Factory クラスは MyClass を継承したクラスであれば何を生成するか知らなくてよい」

    という原則に基づいたジェネリックとタイプセーフを使った実装であって、
    MyFactory が型パラメータに与えられたクラスを知って処理を切り分ける必要があるなら、
    ここはジェネリッククラスにせずに、別のパターンを考えた方がいいかもしれません。

    私としては、ジェネリックを使う際

    「型パラメータに何を与えられたか、ジェネリッククラス側が知る必要がない」

    ように実装すべきだと考えております。

    どうやら私と同じく Delphi 使いだった方らしいですが、
    Delphi のクラス参照とジェネリックを混同して考えるのは、よした方がよさそうです。
    2009年10月29日 9:23
    モデレータ
  • 作れないことも無いけどIListとして扱うぐらいしか出来ませんよ

    class Program
    {
        static void Main(string[] args)
        {
            //generic型パラメータの型
            System.Type parameterType  = typeof(DummyClass);
    
            // List<Dummy>のTypeを作成
            System.Type genericBaseType = typeof(System.Collections.Generic.List<>);
            Type genericType = genericBaseType.MakeGenericType(parameterType);
    
            // List<DummyClass>のインスタンス作成
            IList list = (IList)System.Activator.CreateInstance(genericType);
    
            // 以下テスト
            Type listType = list.GetType();
            Console.WriteLine("ジェネリックになってる? " + listType.IsGenericType);
            Console.WriteLine("*** ジェネリックのパラメータを列挙 ***");
            foreach (Type gen in listType.GetGenericArguments())
            {
                Console.WriteLine(gen.ToString());
            }
            Console.WriteLine("*** ジェネリックのパラメータを列挙 ***");
    
            try
            {
                list.Add(new DummyClass());
                Console.WriteLine("OK : DummyClass2は入るよ");
            }
            catch
            {
                Console.WriteLine("NG : DummyClass2が入らなかったよ");
            }
            try
            {
                list.Add(new DummyClass3());
                Console.WriteLine("NG : DummyClass3が入っちゃったよ");
            }
            catch
            {
                Console.WriteLine("OK : DummyClass3は入らないよ");
            }
            try
            {
                list.Add(new object());
                Console.WriteLine("NG : objectが入っちゃったよ");
            }
            catch
            {
                Console.WriteLine("OK : objectは入らないよ");
            }
    
            Console.WriteLine("型は同じになるの? : " + (typeof(DummyClassList) == genericType).ToString());
            Console.WriteLine("型は同じになるの? : " + (typeof(List<DummyClass>) == genericType).ToString());
        }
    
        internal class DummyClass { }
        internal class DummyClass2 : DummyClass { }
        internal class DummyClass3 { }
        internal class DummyClassList : List<DummyClass> { }
    }
    • 回答としてマーク __test__ 2009年10月29日 10:46
    2009年10月29日 9:23
  • どうやら私と同じく Delphi 使いだった方らしいですが、
    Delphi のクラス参照とジェネリックを混同して考えるのは、よした方がよさそうです。
    返信ありがとうございます。

    Factoryパターンはよく○○の時は××のクラスを作って返すというような使い方をしますよね。
    その○○の部分をintやenum等のパラメータではなくクラスの型その物を使用して分岐させたいと思ったのです。
    判定用の型をわざわざ作りクラスとマッピングするのも無駄のような気がしてならないのです。

    ジェネリックはあくまでその判定用の変数として使ったというわけです。
    2009年10月29日 9:33
  • 作れないことも無いけどIListとして扱うぐらいしか出来ませんよ

    class
     Program
    {
        static
     void
     Main(string
    [] args)
        {
            //generic型パラメータの型
    
            System.Type parameterType  = typeof
    (DummyClass);
    
            // List<Dummy>のTypeを作成
    
            System.Type genericBaseType = typeof
    (System.Collections.Generic.List<>);
            Type genericType = genericBaseType.MakeGenericType(parameterType);
    
            // List<DummyClass>のインスタンス作成
    
            IList list = (IList)System.Activator.CreateInstance(genericType);
    
            // 以下テスト
    
            Type listType = list.GetType();
            Console.WriteLine("ジェネリックになってる? "
     + listType.IsGenericType);
            Console.WriteLine("*** ジェネリックのパラメータを列挙 ***"
    );
            foreach
     (Type gen in
     listType.GetGenericArguments())
            {
                Console.WriteLine(gen.ToString());
            }
            Console.WriteLine("*** ジェネリックのパラメータを列挙 ***"
    );
    
            try
    
            {
                list.Add(new
     DummyClass());
                Console.WriteLine("OK : DummyClass2は入るよ"
    );
            }
            catch
    
            {
                Console.WriteLine("NG : DummyClass2が入らなかったよ"
    );
            }
            try
    
            {
                list.Add(new
     DummyClass3());
                Console.WriteLine("NG : DummyClass3が入っちゃったよ"
    );
            }
            catch
    
            {
                Console.WriteLine("OK : DummyClass3は入らないよ"
    );
            }
            try
    
            {
                list.Add(new
     object
    ());
                Console.WriteLine("NG : objectが入っちゃったよ"
    );
            }
            catch
    
            {
                Console.WriteLine("OK : objectは入らないよ"
    );
            }
    
            Console.WriteLine("型は同じになるの? : "
     + (typeof
    (DummyClassList) == genericType).ToString());
            Console.WriteLine("型は同じになるの? : "
     + (typeof
    (List<DummyClass>) == genericType).ToString());
        }
    
        internal
     class
     DummyClass { }
        internal
     class
     DummyClass2 : DummyClass { }
        internal
     class
     DummyClass3 { }
        internal
     class
     DummyClassList : List<DummyClass> { }
    }
    

    返信ありがとうございます。
    恥ずかしながら、最初、何をやっているのかわかりませんでした。
    こういう事も出来るんですね・・・。
    これを参考に色々と考えてみたいと思います。
    2009年10月29日 9:49
  • ジェネリックはあくまでその判定用の変数として使ったというわけです。
    う~mmm、これは何か違うような気が。。。汗

    それなら Type 型のパラメータかなんか受け取って処理を分岐した方がいいように思います。
    2009年10月29日 9:56
    モデレータ
  • ジェネリックはあくまでその判定用の変数として使ったというわけです。
    う~mmm、これは何か違うような気が。。。汗

    それなら Type 型のパラメータかなんか受け取って処理を分岐した方がいいように思います。
    返信ありがとうございます。
    言われてみればたしかにそうですね・・・。
    素直にType型のパラメータを受け取って分岐したいと思います。

    尚、タイトルにある答えはgekkaさんが記述したコードが回答にふさわしいと思いましたので
    gekkaさんに回答マークを付けさせて頂きました。

    皆様、ありがとうございました。
    2009年10月29日 10:46