web-dev-qa-db-fra.com

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

Le titre est un peu obscur. Ce que je veux savoir, c'est si cela est possible:

string typeName = <read type name from somwhere>;
Type myType = Type.GetType(typeName);

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>();

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

public class MyGenericClass<T>

À l’heure actuelle, le compilateur se plaint que «le type ou l’espace de noms« myType »n’a pas été trouvé.

160
Robert C. Barth

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

using System;
using System.Reflection;

public class Generic<T>
{
    public Generic()
    {
        Console.WriteLine("T={0}", typeof(T));
    }
}

class Test
{
    static void Main()
    {
        string typeName = "System.String";
        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);
193
Jon Skeet

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

14
JaredPar

Mes exigences étaient légèrement différentes, mais j'espère pouvoir aider quelqu'un. J'avais besoin de lire le type d'une config et d'instancier le type générique dynamiquement.

namespace GenericTest
{
    public class Item
    {
    }
}

namespace GenericTest
{
    public class GenericClass<T>
    {
    }
}

Enfin, voici comment vous l'appelez. Définit le type avec un backtick .

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

Quelques autres comment exécuter avec le code de ciseaux. Supposons que vous ayez une classe similaire à

public class Encoder() {
public void Markdown(IEnumerable<FooContent> contents) { do magic }
public void Markdown(IEnumerable<BarContent> contents) { do magic2 }
}

Supposons qu'au moment de l'exécution, vous avez un FooContent 

Si vous pouviez vous lier au moment de la compilation, vous voudriez 

var fooContents = new List<FooContent>(fooContent)
new Encoder().Markdown(fooContents)

Cependant vous ne pouvez pas faire cela au moment de l'exécution. Pour le faire au moment de l'exécution, procédez comme suit:

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

Pour appeler dynamiquement Markdown(IEnumerable<FooContent> contents)

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

Notez l'utilisation de dynamic dans l'appel de méthode. Lors de l'exécution, dynamicList sera List<FooContent> (le code étant également IEnumerable<FooContent>), même si l'utilisation de 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 correspondance de type exacte, il recherchera une méthode de paramètre d'objet et, si aucune correspondance ne correspond, une exception de classeur d'exécution sera déclenchée, avertissant qu'aucune méthode ne correspond.

L’inconvénient évident de cette approche est une énorme perte de sécurité au moment de la compilation. Néanmoins, le code suivant ces lignes vous permettra de fonctionner de manière très dynamique: au moment de l'exécution, le type de saisie est toujours tel que prévu.

2
Chris Marisic

Si vous savez quels types seront passés, vous pouvez le faire sans réfléchir. Une déclaration de commutateur fonctionnerait. Évidemment, cela ne fonctionnerait que dans un nombre limité de cas, mais ce sera beaucoup plus rapide que la réflexion.

public class Type1 { }

public class Type2 { }

public class Generic<T> { }

public class Program
{
    public static void Main()
    {
        var typeName = nameof(Type1);

        switch (typeName)
        {
            case nameof(Type1):
                var type1 = new Generic<Type1>();
                // do something
                break;
            case nameof(Type2):
                var type2 = new Generic<Type2>();
                // do something
                break;
        }
    }
}
0
Todd Skelton

Dans cet extrait, je veux montrer comment créer et utiliser une liste créée de manière dynamique. Par exemple, j'ajoute à la liste dynamique ici.

void AddValue<T>(object targetList, T valueToAdd)
{
    var addMethod = targetList.GetType().GetMethod("Add");
    addMethod.Invoke(targetList, new[] { valueToAdd } as object[]);
}

var listType = typeof(List<>).MakeGenericType(new[] { dynamicType }); // dynamicType is the type you want
var list = Activator.CreateInstance(listType);

AddValue(list, 5);

De même, vous pouvez appeler n'importe quelle autre méthode de la liste.

0
EGN