Comment puis-je utiliser la réflexion pour appeler une méthode générique?
Quelle est la meilleure façon d'appeler une méthode générique lorsque le paramètre type n'est pas connu au moment de la compilation, mais, au contraire, est obtenue de façon dynamique lors de l'exécution?
Considérer l'exemple de code suivant à l'intérieur de l' Example()
méthode, ce qui est le plus concis façon d'invoquer GenericMethod<T>()
à l'aide de la Type
stockées dans le myType
variable?
public class Sample
{
public void Example(string typeName)
{
Type myType = FindType(typeName);
//What goes here to call GenericMethod<T>()?
GenericMethod<myType>(); //This doesn't work
//What changes to call StaticMethod<T>()?
Sample.StaticMethod<myType>(); //This also doesn't work
}
public void GenericMethod<T>()
{
//...
}
public static void StaticMethod<T>()
{
//...
}
}
J'ai essayé de Jon solution et ne pouvait pas le faire fonctionner jusqu'à ce que j'ai fait le générique de la méthode dans ma classe. Je sais qu'un autre Jon a répondu en disant que vous avez besoin de spécifier le bindingflags mais cela n'a pas aider.
Vous avez également besoin d'
La version moderne de cette question: stackoverflow.com/q/2433436/103167
Mortensen - pour info j'ai utilisé des espaces avant le '?' pour séparer les anglais des parties de la non-anglais (C#) les parties; à mon humble avis, supprimer l'espace fait ressembler à l' ? est une partie du code. Si il n'y a pas de code, je serais certainement d'accord avec la suppression de l'espace, mais dans ce cas ...
Vous avez également besoin d'
BindingFlags.Instance
, pas seulement BindingFlags.NonPublic
, pour obtenir le privé/méthode interne.La version moderne de cette question: stackoverflow.com/q/2433436/103167
Mortensen - pour info j'ai utilisé des espaces avant le '?' pour séparer les anglais des parties de la non-anglais (C#) les parties; à mon humble avis, supprimer l'espace fait ressembler à l' ? est une partie du code. Si il n'y a pas de code, je serais certainement d'accord avec la suppression de l'espace, mais dans ce cas ...
OriginalL'auteur Bevan | 2008-10-24
Vous devez vous connecter pour publier un commentaire.
Vous devez utiliser la réflexion pour obtenir la méthode pour commencer, puis "construire" en fournissant des arguments de type avec MakeGenericMethod:
Pour une méthode statique, passer
null
comme premier argument deInvoke
. Ce n'est rien à faire avec des méthodes génériques - c'est tout à fait normal de réflexion.Comme indiqué, un grand nombre de ce qui est plus simple que de C# 4 à l'aide de
dynamic
- si vous pouvez utiliser l'inférence de type, bien sûr. Il ne l'aide pas dans les cas où l'inférence de type n'est pas disponible, comme l'exemple précis de la question.GetMethod()
prend uniquement en compte les publics les méthodes d'instance par défaut, vous devrez peut-êtreBindingFlags.Static
et/ouBindingFlags.NonPublic
.La bonne combinaison d'indicateurs est
BindingFlags.NonPublic | BindingFlags.Instance
(et éventuellementBindingFlags.Static
).Une question se marque dupe de cette se demande comment faire avec des méthodes statiques et techniquement la question ici. génériques.Invoke()'s le premier paramètre doit avoir la valeur null lors de l'appel de méthodes statiques. Le premier paramètre n'est nécessaire que lors de l'appel de méthodes d'instance.
A ajouté que pour la réponse.
J'ai ajouté quelque chose à la réponse - mais notez que pour appeler les méthodes génériques dans la question,
dynamic
n'aide pas parce que l'inférence de type n'est pas disponible. (Il n'y a pas d'arguments, le compilateur peut utiliser pour déterminer le type d'argument.)OriginalL'auteur Jon Skeet
Juste un ajout à la réponse originale à cette question. Alors que cela fonctionne:
C'est aussi un peu dangereux dans que vous perdez au moment de la compilation vérifier
GenericMethod
. Si plus tard vous faire un refactoring et renommerGenericMethod
, ce code ne sera pas un avis et échoue au moment de l'exécution. Aussi, si il n'y a aucun post-traitement de l'assemblée (par exemple, le brouillage ou la suppression de inutilisés méthodes/classes), ce code pourrait rompre trop.Donc, si vous savez que la méthode que vous liez au moment de la compilation, ce qui n'est pas appelé millions de fois la charge du système n'a pas d'importance, je voudrais changer ce code:
Bien que pas très jolie, vous avez un moment de la compilation de référence pour
GenericMethod
ici, et si vous refactoriser, de supprimer ou de faire n'importe quoi avecGenericMethod
, ce code va continuer à travailler, ou au moins pause au moment de la compilation (par exemple, si vous supprimezGenericMethod
).Autre façon de faire la même chose serait de créer une nouvelle classe wrapper, et de créer par
Activator
. Je ne sais pas si il ya une meilleure façon.Eh bien, je suis d'accord pour les utilisations courantes de la réflexion. Mais la question initiale était de savoir comment appeler "GenericMethod<myType>()" Si cette syntaxe a été autorisé, nous n'aurions pas besoin GetMethod (). Mais pour la question "comment dois-je écrire "GenericMethod<myType>"? Je pense que la réponse doit inclure un moyen d'éviter de perdre le temps de compilation lien avec GenericMethod. Maintenant, si cette question est courant ou pas je ne sais pas, mais je sais que j'ai eu exactement ce problème hier, et c'est pourquoi j'ai atterri dans cette question.
Vous pourriez faire
GenMethod.Method.GetGenericMethodDefinition()
au lieu dethis.GetType().GetMethod(GenMethod.Method.Name)
. Il est légèrement plus propre et probablement plus en sécurité.Que signifie "myType" dans votre échantillon?
Maintenant, vous pouvez utiliser
nameof(GenericMethod)
OriginalL'auteur Adrian Gallero
L'appel d'une méthode générique avec un paramètre de type connu qu'à l'exécution peut être grandement simplifiée par l'utilisation d'un
dynamique
type, au lieu de l'API reflection.À l'utilisation de cette technique, le type doit être connue de l'objet réel (et pas seulement une instance de la
Type
classe). Sinon, vous devez créer un objet de ce type ou de l'utilisation de la norme API reflection solution. Vous pouvez créer un objet en utilisant le Activateur.CreateInstance méthode.Si vous voulez appeler une méthode générique, qu'en "normal" d'utilisation aurait eu son type inféré, alors qu'il vient tout simplement de la coulée de l'objet de type inconnu à
dynamic
. Voici un exemple:Et voici la sortie de ce programme:
Process
générique est une méthode d'instance qui écrit le vrai type de l'argument passé (à l'aide de laGetType()
méthode) et le type de paramètre générique (à l'aide detypeof
opérateur).Par moulage de l'objet argument
dynamic
type nous avons reporté fournir le type de paramètre jusqu'à ce que l'exécution. Lorsque leProcess
méthode est appelée avec ledynamic
argument alors le compilateur n'est pas le type de cet argument. Le compilateur génère du code qui au moment de l'exécution de vérifications de la vraie types d'arguments passés (par réflexion) et de choisir la meilleure méthode à appeler. Ici, il n'y a qu'une méthode générique, si elle est appelée avec un paramètre de type.Dans cet exemple, la sortie est la même que si vous avez écrit:
La version avec un type dynamique est nettement plus court et plus facile à écrire. Vous aussi vous ne devriez pas vous soucier de la performance à l'appel de cette fonction plusieurs fois. Le prochain appel de la fonction avec des arguments du même type devrait être plus rapide grâce à la la mise en cache mécanisme de DLR. Bien sûr, vous pouvez écrire du code qui cache invoquée délégués, mais par l'aide de la
dynamic
type que vous obtenez ce comportement pour gratuit.Si le générique de la méthode que vous voulez l'appeler n'avez pas un argument d'un paramétrées type (de sorte que son type de paramètre ne peut pas être déduite), alors vous pouvez envelopper l'invocation de la méthode générique dans une méthode d'aide comme dans l'exemple suivant:
Augmenté de sécurité de type
Ce qui est vraiment bien sur l'utilisation de
dynamic
objet comme un remplacement pour l'utilisation de la réflexion de l'API, c'est que vous ne perdez au moment de la compilation, la vérification de ce type particulier que vous ne savez pas jusqu'à l'exécution. D'autres arguments et le nom de la méthode sont staticly analysés par le compilateur comme d'habitude. Si vous supprimez ou ajoutez plus d'arguments, de modifier leurs types ou de renommer le nom de la méthode, vous obtiendrez une erreur de compilation. Cela ne se produira pas si vous fournissez le nom de la méthode comme une chaîne de caractères dansType.GetMethod
et les arguments que les objets de tableau dansMethodInfo.Invoke
.Ci-dessous est un exemple simple qui illustre la façon dont certaines erreurs peuvent être pris au moment de la compilation (code commenté) et d'autres au moment de l'exécution. Il montre aussi comment le DLR essaie de résoudre la méthode à appeler.
Ici, nous avons de nouveau exécuter une méthode de coulée de l'argument de la
dynamic
type. Seulement la vérification de la première de l'argument type est reportée au moment de l'exécution. Vous obtiendrez une erreur de compilation si le nom de la méthode que vous avez demandé n'existe pas ou si d'autres arguments ne sont pas valides (mauvais nombre d'arguments ou de mauvais types).Quand vous passez le
dynamic
argument d'une méthode, puis cet appel est dernièrement lié. Surcharge de la méthode de résolution qui se passe lors de l'exécution et tente de choisir le meilleur de surcharge. Donc, si vous appelez laProcessItem
méthode à un objet deBarItem
type, alors vous aurez fait appel à la non-méthode générique, parce qu'il est un meilleur match pour ce type. Toutefois, vous obtiendrez une erreur d'exécution lorsque vous passez un argument de laAlpha
type car il n'y a pas de méthode qui peut manipuler cet objet (une méthode générique a la contraintewhere T : IItem
etAlpha
classe n'implémente cette interface). Mais c'est l'ensemble de point. Le compilateur n'a pas l'information que cet appel est valide. Vous en tant que programmeur sait cela, et vous devez vous assurer que ce code s'exécute sans erreur.Type de retour gotcha
Lorsque vous appelez un non-nulle méthode avec un paramètre de type dynamique, son type de retour ne sera probablement
dynamique
trop. Donc, si vous souhaitez modifier l'exemple précédent du présent code:alors le type de l'objet de résultat serait
dynamic
. C'est parce que le compilateur ne sait pas toujours la méthode qui sera appelée. Si vous connaissez le type de retour de l'appel de la fonction, alors vous devriez convertir implicitement pour le type requis de sorte que le reste du code est statiquement typé:Vous obtiendrez une erreur d'exécution si le type ne correspond pas.
En fait, si vous essayez d'obtenir le résultat de la valeur dans l'exemple précédent, vous obtiendrez une erreur d'exécution dans la deuxième itération de boucle. C'est parce que vous avez essayé de sauvegarder la valeur de retour d'une fonction void.
J'ai édité ma réponse à clarifier un peu. C'est parce que générique
ProcessItem
méthode a contrainte générique et n'accepte que les objet qui implémenteIItem
interface. Lorsque vous appelezProcessItem(new Aplha(), "test" , 1);
ouProcessItem((object)(new Aplha()), "test" , 1);
vous obtiendrez une erreur de compilation, mais lors de la coulée dedynamic
vous reporter la vérification de l'exécution.Grande réponse et explication, fonctionne parfaitement pour moi. Beaucoup mieux que la accepté de répondre, plus court à écrire, plus performant et plus sûr.
OriginalL'auteur Mariusz Pawelski
Avec C# 4.0, la réflexion n'est pas nécessaire que le DLR pouvez appeler à l'aide d'exécution types. Depuis l'utilisation du DLR bibliothèque est une sorte de douleur dynamiquement (au lieu de le compilateur C# de la génération de code pour vous), le framework open source Dynamitey (.net standard 1.5) vous donne facilement mis en cache au moment de l'exécution d'accéder à la même appelle le compilateur de générer pour vous.
OriginalL'auteur jbtule
Ajoutant à Adrian Gallero réponse:
L'appel d'une méthode générique de type info déroule en trois étapes.
TLDR: Appel connue méthode générique d'un type d'objet qui peut être accompli par:
où
GenericMethod<object>
est le nom de la méthode à appeler et à n'importe quel type qui satisfait aux contraintes génériques.(Action) correspond à la signature de la méthode à appeler à savoir (
Func<string,string,int>
ouAction<bool>
)L'étape 1 est d'obtenir le MethodInfo pour le générique de la définition de la méthode de
Méthode 1: Utiliser GetMethod() ou GetMethods() avec les types appropriés ou de liaison drapeaux.
Méthode 2: Créer un délégué, obtenir le MethodInfo objet et ensuite appeler GetGenericMethodDefinition
De l'intérieur de la classe qui contient des méthodes:
De l'extérieur de la classe qui contient des méthodes:
En C#, le nom d'une méthode, c'est à dire "ToString" ou "GenericMethod" fait référence à un groupe de méthodes qui peuvent contenir une ou plusieurs méthodes. Jusqu'à ce que vous offrent les types des paramètres de la méthode, on ne sait pas qui
la méthode que vous faites allusion.
((Action)GenericMethod<object>)
se réfère à la délégué pour une méthode spécifique.((Func<string, int>)GenericMethod<object>)
se réfère à une autre surcharge de GenericMethod
Méthode 3: Créer une lambda expression contenant un appel de méthode d'expression, d'obtenir le MethodInfo objet, puis GetGenericMethodDefinition
Cela revient à
Créer une expression lambda où le corps est un appel à votre méthode souhaitée.
Extraire le corps et jeté à MethodCallExpression
Obtenir le générique de la définition de la méthode de la méthode
L'étape 2 est l'appel de MakeGenericMethod pour créer une méthode générique avec le type approprié(s).
L'étape 3 est en invoquant la méthode avec les arguments appropriés.
OriginalL'auteur Grax
Personne n'a fourni la "classique Réflexion" solution, donc, ici, est un exemple de code:
Ci-dessus
DynamicDictionaryFactory
classe a une méthodeCreateDynamicGenericInstance(Type keyType, Type valueType)
et il crée et retourne un IDictionary exemple, les types de dont les clés et les valeurs sont exactement les spécifiée sur l'appel
keyType
etvalueType
.Voici un exemple complet comment appeler cette méthode pour instancier et utiliser un
Dictionary<String, int>
:Lorsque la console ci-dessus l'application est exécutée, on obtient le bon résultat attendu:
OriginalL'auteur Dimitre Novatchev
C'est mes 2 cents basé sur Grax réponse, mais avec deux paramètres requis pour une méthode générique.
Supposons que votre méthode est définie comme suit dans un Helpers classe:
Dans mon cas, U est toujours de type une collection observable stockage objet de type T.
Que j'ai mon des types prédéfinis, j'ai d'abord créer le "dummy" des objets qui représentent la collection observable (U) et de l'objet stocké dans celui-ci (T) et qui sera utilisé ci-dessous pour obtenir leur type lors de l'appel de la Faire
Puis d'appeler le GetMethod pour trouver votre fonction Générique:
Jusqu'à présent, l'appel ci-dessus est à peu près identique à ce qui a été expliqué ci-dessus, mais avec une petite différence quand vous avez besoin d'avoir à passer plusieurs paramètres.
Vous avez besoin pour passer d'un Type tableau[] à la MakeGenericMethod fonction qui contient le "dummy" objets " de types qui ont été créer ci-dessus:
Une fois cela fait, vous devez appeler la méthode Invoke comme mentionné ci-dessus.
Et vous avez terminé. Fonctionne à merveille!
Mise à JOUR:
@Bevan mis en évidence, je n'ai pas besoin de créer un tableau lors de l'appel de la MakeGenericMethod fonctionner comme il faut dans les paramètres et je n'ai pas besoin de créer un objet afin d'obtenir les types que je peux simplement passer les types directement à cette fonction. Dans mon cas, depuis que j'ai les types prédéfinis dans une autre classe, j'ai simplement changé mon code:
myClassInfo contient 2 propriétés de type
Type
que j'ai mis lors de l'exécution basée sur une valeur d'enum transmis au constructeur et me fournir les types pertinents que j'utilise ensuite dans le MakeGenericMethod.Merci encore pour mettre en évidence cette @Bevan.
MakeGenericMethod()
ont le params mot clé de sorte que vous n'avez pas besoin de créer un tableau, ni avez-vous besoin pour créer des instances pour obtenir les types demethodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject))
serait suffisant.OriginalL'auteur Thierry