web-dev-qa-db-fra.com

Initialisation d'une variable générique à partir d'une variable de type C #

J'ai une classe qui prend un type générique dans le cadre de son initialisation.

public class AnimalContext<T>
{
    public DoAnimalStuff()
    {
        //AnimalType Specific Code
    }
}

Ce que je peux faire maintenant

AnimalContext<Donkey> donkeyContext = new AnimalContext<Donkey>();
AnimalContext<Orca> orcaContext = new AnimalContext<Orca>();

Mais ce que j'ai besoin/envie de faire est de pouvoir déclarer un AnimalContext initialisé à un type qui n'est connu qu'au moment de l'exécution. Par exemple,

Animal a = MyFavoriteAnimal(); //returns an instance of a class 
                               //implementing an animal
AnimalContext<a.GetType()> a_Context = new AnimalContext<a.GetType()>();
a_Context.DoAnimalStuff();

Est-ce seulement possible? Je n'arrive pas à trouver une réponse à cela en ligne.

39
Peter Lange

Ce que vous entendez par cette partie est possible:

new AnimalContext<a.GetType()>();

Évidemment, cette syntaxe exacte est erronée, et nous y reviendrons, mais il est possible de construire une instance d'un type générique à runtime lorsque vous ne connaissez pas le tapez les paramètres jusqu'à runtime.

Ce que vous entendez par cette partie est pas:

AnimalContext<a.GetType()> a_Context

Autrement dit, il est impossible de taper une variable en tant que type générique si vous ne connaissez pas les paramètres de type à au moment de la compilation. Les génériques sont des constructions au moment de la compilation et reposent sur la disponibilité des informations de type à au moment de la compilation. Compte tenu de cela, vous perdez tous les avantages des génériques si vous ne connaissez pas les types au moment de la compilation.

Maintenant, pour construire une instance d'un type générique à l'exécution lorsque vous ne connaissez pas le type jusqu'à l'exécution, vous pouvez dire:

var type = typeof(AnimalContext<>).MakeGenericType(a.GetType());
var a_Context = Activator.CreateInstance(type);   

Notez que le type au moment de la compilation de a_context est object. Vous devrez lancer a_context à un type ou une interface qui définit les méthodes auxquelles vous devez accéder. Souvent, ce que vous verrez ici est le type générique AnimalContext<T> implémente une interface (disons IAnimalContext) o hérite d'une classe de base non générique (disons AnimalContext) qui définit les méthodes dont ils ont besoin (vous pouvez donc cast a_context à l'interface ou à la classe de base non générique). Une autre alternative consiste à utiliser dynamic. Mais encore une fois, gardez à l'esprit, vous avez aucun des avantages des types génériques pour ce faire.

36
jason

Vous pouvez utiliser la réflexion avec un type générique en utilisant la méthode MakeGenericType et profiter du mot clé dynamic:

var type = typeof (AnimalContext<>).MakeGenericType(a.GetType());
dynamic a_Context = Activator.CreateInstance(type);

Vous pouvez donc appeler:

a_Context.DoAnimalStuff();

Ou utilisez à nouveau la réflexion pour appeler la méthode:

type.GetMethod("DoAnimalStuff").Invoke(a_Context, null);
9
cuongle

Vous devez créer le type à l'aide de Reflection, puis appeler ce type. Quelque chose comme:

Animal a = MyFavoriteAnimal();
var contextType = typeof(EsbRepository<>).MakeGenericType(a.GetType());

dynamic context = Activator.CreateInstance(contextType);
context.DoAnimalStuff();

L'utilisation de dynamique signifie que la variable de contexte sera évaluée au moment de l'exécution, ce qui vous permet d'appeler la méthode DoAnimalStuff.

6
SCB