CreateDelegate avec des types inconnus

J’essaie de créer un délégué pour la lecture / écriture des propriétés d’un type inconnu de classe au moment de l’exécution.

J’ai une classe générique Main et une méthode qui ressemble à ceci:

 Delegate.CreateDelegate(typeof(Func), get) 

get est un MethodInfo de la propriété à lire. Le problème est que lorsque la propriété renvoie int (je suppose que cela se produit pour les types valeur), le code ci-dessus lève ArgumentException car la méthode ne peut pas être liée. En cas de ficelle ça marche bien.

Pour résoudre le problème, j’ai modifié le code afin que le type de délégué correspondant soit généré à l’aide de MakeGenericType . Alors maintenant, le code est:

 Type func = typeof(Func); Type generic = func.MakeGenericType(typeof(T), get.ReturnType); var result = Delegate.CreateDelegate(generic, get) 

Le problème maintenant est que l’instance de délégué generic créée doit donc utiliser DynamicInvoke ce qui est aussi lent que l’utilisation d’une reflection pure pour lire le champ.

Ma question est donc la suivante: pourquoi le premier extrait de code échoue-t-il avec les types de valeur? Selon MSDN, cela devrait fonctionner car il dit que

Le type de retour d’un délégué est compatible avec le type de retour d’une méthode si le type de retour de la méthode est plus ressortingctif que le type de retour du délégué

et comment exécuter le délégué dans le deuxième extrait de manière à ce qu’il soit plus rapide que la reflection.

Merci.

Voici un moyen de résoudre votre problème. Créez une méthode générique:

 public static Func MakeDelegate(MethodInfo @get) { var f = (Func)Delegate.CreateDelegate(typeof(Func), @get); return t => f(t); } 

De cette façon, le compilateur de C # se charge d’insérer la casse nécessaire (le cas échéant) pour convertir f(t) (de type U ) en object . Vous pouvez maintenant utiliser la reflection pour appeler cette méthode MakeDelegate avec U défini sur @get.ReturnType . Ce que vous récupérerez sera un Func pouvant être appelé sans recourir à DynamicInvoke .

Votre code d’origine ne peut fonctionner que pour les types de référence. C’est pourquoi la chaîne de caractères n’était pas un problème, elle dérive directement de System.Object. Qu’un type de valeur dérive de ValueType et que Object est une belle illusion sur papier mais nécessite en réalité du code. Le compilateur C # émet automatiquement ce code, il nécessite une conversion de boxe. C’est la partie qui manque ici, il n’y a pas de conversion à l’exécution d’exécution de int en object sans l’ opcode BOX .

Vous pouvez obtenir cet opcode dans votre code, mais vous devrez utiliser System.Reflection.Emit.

Avant de vous y rendre, vérifiez d’abord si ce que vous avez maintenant est en réalité trop lent. Le coût de la reflection consiste à extraire les métadonnées de l’assemblée. Cela a été fait lorsque vous avez créé le délégué, les informations de type sont ensuite mises en cache.

Votre appel échoue parce que vous avez besoin d’un object et non d’un type de valeur (comme INT) – Func n’est évidemment pas un Func – il ne fonctionnera avec aucun vt comme double ou bool. Soit retourner un Int box (ou tout vt que vous avez). ou (peut-être mieux) utiliser l’API émet reflection.

En utilisant des classes émises par reflection Vous pouvez créer des méthodes dynamics et les enregistrer en tant que delegates, ou créer des delegates dynamics et les enregistrer dans une partie de Votre structure. Vous pouvez le faire seulement une fois (peut-être une fois par exécution), le stocker dans un dict et l’invoquer si nécessaire.

J’espère que ça aide. luke

Est-il possible pour vous de restreindre la méthode générique afin qu’elle ne fonctionne qu’avec des types de référence et d’en créer une autre pour ne travailler qu’avec des types de valeur et de décider de la fonctionnalité à utiliser en conséquence?