2010-02-28 11 views
3

J'essaie de créer une représentation déléguée de constructeur en émettant une méthode dynamique, qui doit correspondre à cette signature très "faiblement typée" afin qu'elle puisse être utilisée avec n'importe quel type de constructeur paramétré constructeur:C# Déléguer une méthode dynamique déléguée pour charger un problème de constructeur paramétré

public delegate Object ParamsConstructorDelegate(params object[] parameters); 

et le code pour cette création du délégué ressemble (notez ceci est pour Silverlight)

public static ParamsConstructorDelegate CreateDelegate(ConstructorInfo constructor) 
    { 
     Guard.ArgumentNotNull(constructor, "constructor"); 
     Guard.ArgumentValue(constructor.GetParameters().Length == 0, MUSTBE_PARAMETERIZED_CONSTRUCTOR); 

     var _argumentTypes = new Type[] { typeof(object[]) }; 
     var _parameters = constructor.GetParameters(); 
     var _parameterTypes = _parameters.Select((p) => p.ParameterType).ToArray(); 

     var _sourceType = constructor.DeclaringType; 
     var _method = new DynamicMethod(constructor.Name, _sourceType, _argumentTypes); 
     var _gen = _method.GetILGenerator(); 

     for (var _i = 0; _i < _parameters.Length; _i++) 
     { 
      if (_parameters[_i].IsOut || _parameterTypes[_i].IsByRef) 
      { 
       if (_i < 128) 
       { 
        _gen.Emit(OpCodes.Ldarga_S, (byte)_i); 
       } 
       else 
        _gen.Emit(OpCodes.Ldarga, _i); 
      } 
      else 
      { 
       switch (_i) 
       { 
        case 0: 
         _gen.Emit(OpCodes.Ldarg_0, _i); 
         break; 
        case 1: 
         _gen.Emit(OpCodes.Ldarg_1, _i); 
         break; 
        case 2: 
         _gen.Emit(OpCodes.Ldarg_2, _i); 
         break; 
        case 3: 
         _gen.Emit(OpCodes.Ldarg_3, _i); 
         break; 
        default: 
         if (_i < 128) 
          _gen.Emit(OpCodes.Ldarg_S, (byte)_i); 
         else 
          _gen.Emit(OpCodes.Ldarg, _i); 
         break; 
       } 
      } 
     } 
     _gen.Emit(OpCodes.Newobj, constructor); 
     _gen.Emit(OpCodes.Ret); ; 

     return (ParamsConstructorDelegate)_method.CreateDelegate(typeof(ParamsConstructorDelegate)); 
    } 

maintenant, je reçois une « opération pourrait déstabiliser l'exécution. » exception de vérification, évidemment IL est faux, alors j'espère que quelqu'un pourrait me corriger.

Merci

+0

Le préfixe underscore pour les locaux est un style que je n'ai pas vu beaucoup de. –

Répondre

5

Je peux voir deux problèmes; Tout d'abord vous n'avez pas besoin de _i pour les Ldarg_0 à Ldarg_3 cas (c'est implicite). Deuxièmement - votre délégué seulement a un arg (le tableau). Vous allez avoir besoin de sortir les éléments du tableau et de lancer - quelque chose comme ci-dessous (qui ne gère que la valeur par passe, pour ref/out vous devrez définir un local et utiliser stloc/ldloca/etc) :

using System; 
using System.Reflection.Emit; 
public delegate object ParamsConstructorDelegate(params object[] parameters); 
public class Foo 
{ 
    string s; 
    int i; 
    float? f; 
    public Foo(string s, int i, float? f) 
    { 
     this.s = s; 
     this.i = i; 
     this.f = f; 
    } 
} 

static class Program 
{ 
    static void Main() 
    { 
     var ctor = Build(typeof(Foo)); 
     Foo foo1 = (Foo)ctor("abc", 123, null); 
     Foo foo2 = (Foo)ctor(null, 123, 123.45F); 
    } 
    static ParamsConstructorDelegate Build(Type type) 
    { 
     var mthd = new DynamicMethod(".ctor", type, 
      new Type[] { typeof(object[]) }); 
     var il = mthd.GetILGenerator(); 
     var ctor = type.GetConstructors()[0]; // not very robust, but meh... 
     var ctorParams = ctor.GetParameters(); 
     for (int i = 0; i < ctorParams.Length; i++) 
     { 
      il.Emit(OpCodes.Ldarg_0); 
      switch (i) 
      { 
       case 0: il.Emit(OpCodes.Ldc_I4_0); break; 
       case 1: il.Emit(OpCodes.Ldc_I4_1); break; 
       case 2: il.Emit(OpCodes.Ldc_I4_2); break; 
       case 3: il.Emit(OpCodes.Ldc_I4_3); break; 
       case 4: il.Emit(OpCodes.Ldc_I4_4); break; 
       case 5: il.Emit(OpCodes.Ldc_I4_5); break; 
       case 6: il.Emit(OpCodes.Ldc_I4_6); break; 
       case 7: il.Emit(OpCodes.Ldc_I4_7); break; 
       case 8: il.Emit(OpCodes.Ldc_I4_8); break; 
       default: il.Emit(OpCodes.Ldc_I4, i); break; 
      } 
      il.Emit(OpCodes.Ldelem_Ref); 
      Type paramType = ctorParams[i].ParameterType; 
      il.Emit(paramType.IsValueType ? OpCodes.Unbox_Any 
       : OpCodes.Castclass, paramType); 
     } 
     il.Emit(OpCodes.Newobj, ctor); 
     il.Emit(OpCodes.Ret); 
     return (ParamsConstructorDelegate) 
      mthd.CreateDelegate(typeof(ParamsConstructorDelegate)); 
    } 
} 

pour info - Je suis paresseux - si je veux savoir ce que iL je l'écris écrire en C#, puis le charger dans le réflecteur. Par exemple, pour ce faire, j'ai écrit une méthode:

static object CreateFoo(object[] vals) 
{ 
    return new Foo((string)vals[0], (int)vals[1], (float?)vals[2]); 
} 

et reprises de là

+0

Merci pour la réponse rapide Marc, est-il possible que vous me montriez comment étendre le tableau et pousser les valeurs dans le constructeur - parce que je ne suis pas sûr de savoir comment juxtaposer les valeurs du tableau et ensuite en utilisant IL. Merci encore. – Orktane

+0

@Orktane avez-vous vu la mise à jour? –

+0

Je viens de voir ça et ça marche tout simplement génial. De plus, merci de m'avoir montré le constructeur statique que vous avez inversé, c'est exactement la partie qui me manquait et tout va maintenant. Appréciez votre aide, applaudissements. – Orktane

1

Je trouve qu'il est très difficile de comprendre ce que le message d'erreur « l'opération pourrait déstabiliser l'exécution » signifie lors de l'utilisation Reflection.Emit - le CLR ne donne pas beaucoup d'informations utiles ici. Une astuce que vous pouvez utiliser pour obtenir plus d'informations sur le problème consiste à modifier votre code, afin qu'il émette le code à un assemblage temporaire (en plus d'émettre un délégué dynamique) lors de l'exécution en mode débogage.

Ensuite, vous pouvez utiliser l'outil peverify (à partir de la ligne de commande Visual Studio), ce qui vous donne habituellement plus d'informations sur les problèmes avec le code IL généré:

> peverify assembly.dll 

Vous aurez besoin d'utiliser des classes comme AssemblyBuilder et ModuleBuilder pour produire un assemblage. Vous pouvez ensuite exécuter la partie principale (qui utilise ILGenerator) deux fois pour générer un délégué dynamique (pour l'exécution réelle) et un assemblage temporaire (pour le débogage). Je crois que peverify vous donne une bien meilleure information.

+0

Tomas, un truc très utile autour de IL et des délégués, parce que vraiment ces exceptions sont aussi utiles que de dire que vous avez foiré quelque part, maintenant allez y remédier :) Eh bien, pour l'instant Marc ci-dessus m'a sorti, mais merci beaucoup pour la pointe je vais le garder à l'esprit .. – Orktane