web-dev-qa-db-fra.com

Comment créer dynamiquement un objet C # générique en utilisant la réflexion?

En C #, j'ai l'objet suivant:

public class Item
{ }

public class Task<T>
{ }

public class TaskA<T> : Task<T>
{ }

public class TaskB<T> : Task<T>
{ }

Je veux créer dynamiquement TaskA ou TaskB en utilisant la réflexion C # (Activator.CreateInstance). Cependant, je ne connais pas le type avant, je dois donc créer dynamiquement TaskA en fonction d'une chaîne telle que "namespace.TaskA" ou "namespace.TaskAB".

124
Jeff

Découvrez ceci article et ceci exemple simple . Traduction rapide de même pour vos cours ...

var d1 = typeof(Task<>);
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Par votre édition: Pour ce cas, vous pouvez le faire ...

var d1 = Type.GetType("GenericTest.TaskA`1"); // GenericTest was my namespace, add yours
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Pour voir où je suis arrivé avec backtick1 pour le nom de la classe générique, voir cet article .

Remarque: si votre classe générique accepte plusieurs types, vous devez inclure les virgules lorsque vous omettez les noms de type, par exemple:

Type type = typeof(IReadOnlyDictionary<,>);
232
JP Alioto

En effet, vous ne pourriez pas écrire la dernière ligne.

Mais vous ne voulez probablement pas créer l'objet, juste pour le plaisir de le créer. Vous voudrez probablement appeler une méthode sur votre instance nouvellement créée.

Vous aurez alors besoin de quelque chose comme une interface:

public interface ITask 
{
    void Process(object o);
}

public class Task<T> : ITask
{ 
   void ITask.Process(object o) 
   {
      if(o is T) // Just to be sure, and maybe throw an exception
        Process(o as T);
   }

   public void Process(T o) { }
}

et appelez-le avec:

Type d1 = Type.GetType("TaskA"); //or "TaskB"
Type[] typeArgs = { typeof(Item) };
Type makeme = d1.MakeGenericType(typeArgs);
ITask task = Activator.CreateInstance(makeme) as ITask;

// This can be Item, or any type derived from Item
task.Process(new Item());

Dans tous les cas, vous ne serez pas statiquement transtypé sur un type que vous ne connaissez pas auparavant ("makeme" dans ce cas). ITask vous permet d’atteindre votre type de cible.

Si ce n'est pas ce que vous voulez, vous devrez probablement préciser un peu plus précisément ce que vous essayez d'atteindre.

7
Jerome Laban

Il me semble que la dernière ligne de votre exemple de code devrait simplement être:

Task<Item> itsMe = o as Task<Item>;

Ou est-ce que je manque quelque chose?

3
Ben M

Je sais que cette question est résolue mais, pour le bénéfice de tous ceux qui la liront; si vous avez tous les types impliqués en tant que chaînes, vous pouvez le faire en tant que ligne unique:

IYourInterface o = (Activator.CreateInstance(Type.GetType("Namespace.TaskA`1[OtherNamespace.TypeParam]") as IYourInterface);

Chaque fois que j'ai fait ce genre de chose, j'ai une interface que je souhaite utiliser pour le code ultérieur. J'ai donc converti l'instance créée en une interface.

1
A Aiston

Assurez-vous que vous le faites pour une bonne raison. Une fonction simple comme celle qui suit permet le typage statique et permet à votre IDE de faire des tâches telles que "Rechercher les références" et Refactor -> Renommer.

public Task <T> factory (String name)
{
  Task <T> result;

  if (name.CompareTo ("A") == 0)
  {
    result = new TaskA ();
  }
  else if (name.CompareTo ("B") == 0)
  {
    result = new TaskB ();
  }

  return result;
}
1
clemahieu