128 votes

Comment créer dynamiquement des génériques objet de C# à l'aide de 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 à l'aide de C# réflexion (Activateur.CreateInstance). Cependant, je ne sais pas le type avant de la main, j'ai donc besoin de créer dynamiquement TaskA basée sur la chaîne comme "espace de noms.TaskA" ou "espace de noms.TaskAB".

237voto

JP Alioto Points 33482

Découvrez cet article et ce simple exemple. Traduction rapide de même pour vos classes ...

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

Par votre edit: 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 venu avec backtick1 pour le nom de la classe générique, voir cet article.

8voto

Jerome Laban Points 1856

En effet, vous ne seriez pas en mesure d'écrire la dernière ligne.

Mais vous ne voulez probablement pas à créer l'objet, juste pour le plaisir ou le créer. Probablement, vous voulez 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 de l'appeler 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 en fonte pour un type que vous ne connaissez pas à l'avance ("makeme" dans ce cas). ITask vous permet de vous rendre à votre type de cible.

Si ce n'est pas ce que vous voulez, vous aurez probablement besoin d'être un peu plus précis dans ce que vous essayez d'atteindre avec cette.

3voto

Ben M Points 14458

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

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

Ou ai-je raté quelque chose?

1voto

A Aiston Points 477

Je sais que cette question est réglée, mais, pour le bénéfice de quelqu'un d'autre lecture; si vous avez tous les types impliqués comme des chaînes, vous pouvez faire cela comme un liner:

IYourInterface o = (Activateur.CreateInstance(De Type.GetType("Espace De Noms.TaskA`1[OtherNamespace.TypeParam]") comme IYourInterface);

Chaque fois que j'ai fait ce genre de chose, j'ai eu une interface qui je voulais le code suivant à les utiliser, donc j'ai coulé l'instance créée à une interface.

1voto

clemahieu Points 1237

Assurez-vous que vous êtes en train de faire ce pour une bonne raison, une fonction simple comme les suivantes permettrait à typage statique et permet à votre IDE de faire des choses comme "Trouver des Références" et Refactoriser -> 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;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X