web-dev-qa-db-fra.com

Déclarer dynamiquement une instance de type générique

Est-il possible de déclarer une instance d'un générique sans connaître le type au moment de la conception?

Exemple:

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

où le type de i pourrait être n'importe quoi, au lieu d'avoir à faire:

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

Si vous ne connaissez pas le type au moment de la compilation, mais que vous voulez le type réel (c'est-à-dire pas List<object>) et vous n'êtes pas dans une méthode/type générique avec le paramètre de type approprié, alors vous devez utiliser la réflexion.

Pour simplifier la réflexion, j'ai parfois introduit un nouveau type ou une nouvelle méthode générique dans mon propre code, donc je peux l'appeler par réflexion, mais ensuite simplement utiliser des génériques normaux après cela. Par exemple:

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;
}

Bien sûr, vous ne pouvez pas faire grand-chose avec la liste par la suite si vous ne connaissez pas le type ... c'est pourquoi ce genre de chose tombe souvent. Pas toujours cependant - j'ai utilisé quelque chose comme ce qui précède à quelques reprises, où le système de types ne me permet tout simplement pas d'exprimer tout ce dont j'ai besoin de manière statique.

EDIT: Notez que bien que j'appelle Type.GetMethod dans le code ci-dessus, si vous vouliez l'exécuter beaucoup, vous voudrez probablement l'appeler une fois - après tout, la méthode ne changera pas. Vous pourrez peut-être le rendre statique (vous pourriez dans le cas ci-dessus) et vous voudrez probablement le rendre privé aussi. Je l'ai laissé comme méthode d'instance publique pour la simplicité de l'appel GetMethod dans un exemple de code - sinon, vous devrez spécifier les indicateurs de liaison appropriés.

46
Jon Skeet

Je pense que le mieux que vous puissiez faire est quelque chose comme ceci:

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

Si vous ne connaissez pas le type au moment de la conception, je dirais que vous avez une liste d'OBJETS (la classe de base pour tous les autres types).

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

Vous pouvez également utiliser Activator.CreateInstance. Exemple d'extrait de code:

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

À peu près sûr que vous pouvez le faire, ils n'ont pas besoin d'être corrigés au moment de la compilation, comme les modèles en c ++.

Un exemple similaire ici: http://geekswithblogs.net/marcel/archive/2007/03/24/109722.aspx

0
seanb

Voir la réponse de la question similaire "Créer dynamiquement un type générique pour le modèle" . La seule différence est qu'ils génèrent le type à partir de la ligne de commande, le reste que vous devriez pouvoir adapter à vos besoins.

En passant, vous ne pouvez pas appeler typeof sur une instance - pour obtenir le type d'une instance (par exemple, "i", appelez GetType ():

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

Si vous voulez toujours taper .Add (), .Remove (), do foreach etc., vous pouvez traiter la List comme un "ancien" System.Collections.IList normal, puisque cette interface est heureusement implémentée par List <T>.

Et comme toutes les autres réponses publiées à cette question montrent à peu près toutes les autres façons possibles de créer dynamiquement une instance d'une List <T>, je vais montrer une dernière façon de le faire. J'utilise personnellement cette méthode lors de la création d'instances génériques, lorsque je ne sais vraiment rien sur le type au moment de la compilation et que le type doit être passé sous forme de chaîne, provenant peut-être du fichier de configuration de l'application. Dans cet exemple, T est System.String pour plus de simplicité, mais cela pourrait être n'importe quoi:

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