2009-12-29 26 views
3

Ma question est la suivante:DynamicMethod pour ConstructorInfo.Invoke, que dois-je prendre en compte?

Si je vais construire un quels types d'IL faire l'objet DynamicMethod, ce qui correspond à un appel ConstructorInfo.Invoke, je dois mettre en œuvre pour faire face à tous (ou la plupart) types des arguments, quand je peux garantir que le bon type et le nombre d'arguments va être passé avant que je fasse l'appel?


Contexte

Je suis sur ma 3ème itération de mon conteneur IoC, et fait actuellement un certain profilage pour savoir s'il y a des domaines où je peux facilement raser de grandes quantités de temps utilisé. Une chose que j'ai remarquée est que lors de la résolution d'un type concret, finalement je me retrouve avec un constructeur appelé, en utilisant ConstructorInfo.Invoke, passant dans un tableau d'arguments que j'ai travaillé. Ce que j'ai remarqué est que la méthode invoke a un peu de surcharge, et je me demande si la plupart de ceci est juste des implémentations différentes des mêmes contrôles que je fais. Par exemple, en raison du code correspondant du constructeur, pour trouver un constructeur correspondant pour les noms de paramètres prédéfinis, les types et les valeurs que j'ai passés, il est impossible que cet appel d'invocation particulier ne finisse pas avec quelque chose il devrait être capable de faire face, comme le nombre correct d'arguments, dans le bon ordre, du bon type, et avec des valeurs appropriées.

Lors d'une session de profilage contenant un million d'appels à ma méthode de détermination, puis la remplacer par une mise en œuvre DynamicMethod qui imite l'appel Invoke, les horaires de profilage était comme ceci:

  • ConstructorInfo.Invoke: 1973ms
  • DynamicMethod: 93ms

cela représente environ 20% de la durée totale de cette application de profilage. En d'autres termes, en remplaçant l'appel ConstructorInfo.Invoke par un DynamicMethod qui fait de même, je suis capable de réduire de 20% le temps d'exécution des services de base en usine (tous les appels de résolution se terminent par un appel constructeur).

Je pense que c'est assez important, et justifie un examen plus approfondi de combien de travail serait de construire un générateur DynamicMethod stable pour les constructeurs dans ce contexte. Ainsi, la méthode dynamique prendrait un tableau d'objets et renverrait l'objet construit, et je connais déjà l'objet ConstructorInfo en question.

Par conséquent, il semble que la méthode dynamique serait composée des éléments suivants IL:

l001: ldarg.0  ; the object array containing the arguments 
l002: ldc.i4.0  ; the index of the first argument 
l003: ldelem.ref ; get the value of the first argument 
l004: castclass T ; cast to the right type of argument (only if not "Object") 
(repeat l001-l004 for all parameters, l004 only for non-Object types, 
varying l002 constant from 0 and up for each index) 
l005: newobj ci ; call the constructor 
l006: ret 

Y at-il autre chose que je dois considérer?Notez que je suis conscient que la création de méthodes dynamiques ne sera probablement pas disponible lors de l'exécution de l'application en "mode d'accès réduit" (parfois le cerveau n'abandonnera pas ces termes), mais dans ce cas, je peux facilement détecter cela et appeler simplement le constructeur d'origine comme précédemment, avec le surcoût et tout.

Répondre

1

Pour les types de valeur, l'étape l004 doit être l004: unbox.any T. La meilleure façon de déterminer le bon IL que vous devez générer est de regarder ce qui est généré par le compilateur C# en utilisant du code de test.

static void Test(object[] args) 
{ 
    TestTarget((string)args[0], (int)args[1], (DateTime?)args[2]); 
} 

static void TestTarget(string s, int i, DateTime? dt){} 

rassemble à:

 
L_0000: ldarg.0 
L_0001: ldc.i4.0 
L_0002: ldelem.ref 
L_0003: castclass string 
L_0008: ldarg.0 
L_0009: ldc.i4.1 
L_000a: ldelem.ref 
L_000b: unbox.any int32 
L_0010: ldarg.0 
L_0011: ldc.i4.2 
L_0012: ldelem.ref 
L_0013: unbox.any [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime> 
L_0018: call void Program::TestTarget(string, int32, valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>) 
L_001d: ret 
+0

Donc, fondamentalement, puisque j'ai l'objet Type pour chaque paramètre, si le Type.IsValueType renvoie true, au lieu de TYPE castclass, j'utilise unbox.any TYPE à la place? –

+0

Oui, cela devrait suffire à couvrir tous les types de paramètres. –

+0

Merci, je vais ajouter un tas de tests unitaires pour vérifier toutes les combinaisons que je peux penser, mais cela semble que ça pourrait juste fonctionner :) –

0

Il existe des bibliothèques disponibles pour le rendre plus facile (et plus rapide) de travailler avec la réflexion. Par exemple, Fasterflect peut générer IL pour invoquer n'importe quel constructeur - tout ce que vous avez à faire est de lui passer les arguments que vous voulez utiliser sur le constructeur.

// note: class must have constructor with (int,string,string) signature 
object obj = someType.CreateInstance(new { id=1, name="jens", foo="bar" }); 

La bibliothèque est également capable de sonder pour un constructeur approprié d'appeler, au cas où vous ne disposez pas d'un ensemble de paramètres qui correspondent exactement à un constructeur.

// try to map id, name and foo to constructor parameters 
// allows changing the order and permit fallback to setting fields/properties 
// e.g. might result in call to ctor(string,string) and set field "id" 
object obj = someType.TryCreateInstance(new { id=1, name="jens", foo="bar" }); 

Avis de non-responsabilité: Je suis impliqué dans ledit projet en tant que contributeur.