web-dev-qa-db-ja.com

ジェネリック型インスタンスを動的に宣言する

デザイン時に型を知らなくてもジェネリックのインスタンスを宣言することは可能ですか?

例:

Int i = 1;
List<typeof(i)> list = new List<typeof(i)>();

ここで、タイプする必要はありません。

List<int> list = new List<int();
30
benPearce

コンパイル時に型がわからないが、実際の型が必要な場合(つまり、List<object>and適切なタイプパラメータを持つジェネリックメソッド/タイプではない場合、リフレクションを使用する必要があります。

リフレクションを簡単にするために、新しいジェネリック型またはメソッドを自分のコードに導入することもあるので、リフレクションによってそれを呼び出すことができますが、その後は通常のジェネリックを使用します。例えば:

object x = GetObjectFromSomewhere();
// I want to create a List<?> containing the existing
// object, but strongly typed to the "right" type depending
// on the type of the value of x
MethodInfo method = GetType().GetMethod("BuildListHelper");
method = method.MakeGenericMethod(new Type[] { x.GetType() });
object list = method.Invoke(this, new object[] { x });

// Later

public IList<T> BuildListHelper<T>(T item)
{
    List<T> list = new List<T>();
    list.Add(item);
    return list;
}

もちろん、タイプがわからなければ、後でリストを使ってひどいことをすることはできません...そのため、この種のことはしばしば失敗します。常にではありませんが、上記のようなものを数回使用しました。型システムでは、静的に必要なすべてを表現することができないからです。

編集:上記のコードではType.GetMethodを呼び出していますが、それを頻繁に実行する場合は、おそらく一度だけ呼び出したいことに注意してください。結局のところ、メソッドは変更されません。あなたはそれを静的にすることができるかもしれません(あなたは上記の場合にそうすることができます)そしておそらくあなたもそれをプライベートにしたいでしょう。サンプルコードのGetMethod呼び出しを簡略化するために、パブリックインスタンスメソッドとして残しました。それ以外の場合は、適切なバインディングフラグを指定する必要があります。

46
Jon Skeet

あなたができる最高のことは次のようなものだと思います:

static void Main(string[] args)
{
    int i = 1;
    var thelist = CreateList(i);
}

public static List<T> CreateList<T>(T t)
{
    return new List<T>();
}
1
Nathan

設計時に型がわからない場合は、OBJECTS(他のすべての型の基本クラス)のリストがあると思います。

List<object> list = new List<object>();
1
abelenky

Activator.CreateInstanceを使用することもできます。コードスニペットの例:

public class BaseRepository<T> where T : DataContext
{
   protected T _dc;

   public BaseRepository(string connectionString)
   {
      _dc = (T) Activator.CreateInstance(typeof(T), connectionString);
   }

   public void SubmitChanges()
   {
      _dc.SubmitChanges();
   }
}
1
KevinT

C++のテンプレートのように、コンパイル時に修正する必要はありません。

ここに似た例: http://geekswithblogs.net/marcel/archive/2007/03/24/109722.aspx

0
seanb

同様の質問 "Dynamicically Create a Generic Type for Template" の回答を参照してください。唯一の違いは、コマンドラインから型を生成していることと、残りはニーズに適応できることです。

余談ですが、インスタンスでtypeofを呼び出すことはできません-インスタンスのタイプを取得するため(たとえば、 "i"はGetType()を呼び出します):

Type intType = i.GetType();
0
Brian B.

それでも.Add()、. Remove()、foreachなどを入力したい場合は、Listを通常の「古い」System.Collections.IListとして扱うことができます。

そして、この質問に対する他のすべての投稿された回答は、List <T>のインスタンスを動的に作成するために可能な他のほとんどすべての方法を示しているので、それを行う最後の方法を1つ示します。ジェネリックインスタンスを作成するとき、コンパイル時にタイプについて本当に何も知らないときに個人的にこのメソッドを使用します。タイプは、おそらくアプリケーション構成ファイルからの文字列として渡す必要があります。この例では、Tは簡単にするためにSystem.Stringですが、何でもかまいません。

Type T = typeof ( string ); // replace with actual T
string typeName = string.Format (
  "System.Collections.Generic.List`1[[{0}]], mscorlib", T.AssemblyQualifiedName );

IList list = Activator.CreateInstance ( Type.GetType ( typeName ) )
  as IList;

System.Diagnostics.Debug.Assert ( list != null ); //

list.Add ( "string 1" ); // new T
list.Add ( "string 2" ); // new T
foreach ( object item in list )
{
  Console.WriteLine ( "item: {0}", item );
}
0
baretta