Passer un instantané System.Type en tant que paramètre de type pour une classe générique

Le titre est assez obscur. Ce que je veux savoir, c’est si c’est possible:

ssortingng typeName = ; Type myType = Type.GetType(typeName); MyGenericClass myGenericClass = new MyGenericClass(); 

De toute évidence, MyGenericClass est décrit comme suit:

 public class MyGenericClass 

À l’heure actuelle, le compilateur se plaint de ce que «Le type ou l’espace de noms« myType »est introuvable». Il doit y avoir un moyen de le faire.

Vous ne pouvez pas faire cela sans réfléchir. Cependant, vous pouvez le faire avec reflection. Voici un exemple complet:

 using System; using System.Reflection; public class Generic { public Generic() { Console.WriteLine("T={0}", typeof(T)); } } class Test { static void Main() { ssortingng typeName = "System.Ssortingng"; Type typeArgument = Type.GetType(typeName); Type genericClass = typeof(Generic<>); // MakeGenericType is badly named Type constructedClass = genericClass.MakeGenericType(typeArgument); object created = Activator.CreateInstance(constructedClass); } } 

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 genericClass = typeof(IReadOnlyDictionary<,>); Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2); 

Malheureusement non il n’y en a pas. Les arguments génériques doivent pouvoir être résolus au moment de la compilation en 1) soit un type valide ou 2) un autre paramètre générique. Il n’y a aucun moyen de créer des instances génériques basées sur des valeurs d’exécution sans le gros marteau de la reflection.

Mes exigences étaient légèrement différentes, mais j’espère aider quelqu’un. Je devais lire le type d’une configuration et instancier dynamicment le type générique.

 namespace GenericTest { public class Item { } } namespace GenericTest { public class GenericClass { } } 

Enfin, voici comment vous l’appelez. Définissez le type avec un backtick .

 var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest"); var a = Activator.CreateInstance(t); 

Quelques ajouts sur comment utiliser le code ciseaux. Supposons que vous ayez une classe similaire à

 public class Encoder() { public void Markdown(IEnumerable contents) { do magic } public void Markdown(IEnumerable contents) { do magic2 } } 

Supposons qu’à l’exécution, vous avez un FooContent

Si vous étiez capable de lier au moment de la compilation, vous voudriez

 var fooContents = new List(fooContent) new Encoder().Markdown(fooContents) 

Cependant, vous ne pouvez pas le faire lors de l’exécution. Pour ce faire à l’exécution, vous feriez comme suit:

 var listType = typeof(List<>).MakeGenericType(myType); var dynamicList = Activator.CreateInstance(listType); ((IList)dynamicList).Add(fooContent); 

Markdown(IEnumerable contents) dynamicment Markdown(IEnumerable contents)

 new Encoder().Markdown( (dynamic) dynamicList) 

Notez l’utilisation de la dynamic dans l’appel de méthode. À l’exécution, dynamicList sera List (étant également IEnumerable ), car même l’utilisation de la dynamic est toujours liée à un langage fortement typé. Le classeur d’exécution sélectionne la méthode Markdown appropriée. S’il n’y a pas de correspondances de type exactes, il recherchera une méthode de paramètre d’object et si aucune ne correspond, une exception de classeur d’exécution sera déclenchée pour signaler qu’aucune méthode ne correspond.

L’inconvénient évident de cette approche est une énorme perte de sécurité à la compilation. Néanmoins, le code selon ces lignes vous permettra de fonctionner de manière très dynamic, car à l’exécution, vous êtes toujours complètement typé comme vous le souhaitez.