2009-10-20 7 views
5

J'essaie de générer du code qui prend un StringBuilder et écrit les valeurs de toutes les propriétés d'une classe dans une chaîne. J'ai la suite, mais je reçois actuellement un « jeton de méthode non valide » dans le code suivant:Stringbuilder dans CIL (MSIL)

public static DynamicAccessor<T> CreateWriter(T target) //Target class to *serialize* 
    { 
     DynamicAccessor<T> dynAccessor = new DynamicAccessor<T>(); 

     MethodInfo AppendMethod = typeof(StringBuilder).GetMethod("Append", new[] { typeof(Object) }); //Append method of Stringbuilder 

     var method = new DynamicMethod("ClassWriter", typeof(StringBuilder), new[] { typeof(T) }, typeof(T), true); 
     var generator = method.GetILGenerator(); 
     LocalBuilder sb = generator.DeclareLocal(typeof(StringBuilder)); //sb pointer 


     generator.Emit(OpCodes.Newobj, typeof(StringBuilder)); //make our string builder 
     generator.Emit(OpCodes.Stloc, sb);      //make a pointer to our new sb 


     //iterate through all the instance of T's props and sb.Append their values. 
     PropertyInfo[] props = typeof(T).GetProperties(); 
     foreach (var info in props) 
     { 
      generator.Emit(OpCodes.Callvirt, info.GetGetMethod()); //call the Getter 
      generator.Emit(OpCodes.Ldloc, sb);      //load the sb pointer 
      generator.Emit(OpCodes.Callvirt, AppendMethod);  //Call Append 
     } 

     generator.Emit(OpCodes.Ldloc, sb); 
     generator.Emit(OpCodes.Ret);   //return pointer to sb 

     dynAccessor.WriteHandler = method.CreateDelegate(typeof(Write)) as Write; 
     return dynAccessor; 
    } 

Toutes les idées? Merci d'avance :)

+0

(répondu pour commenter) –

Répondre

5

Toutes les propriétés qui sont des types de valeur (int etc) auront besoin de boxe; ou vous devrez utiliser une surcharge Append différente.

aussi:

  • vous devez charger l'objet chaque fois (arg0)
  • StringBuilder.Append est une API couramment; vous devez soit pop la valeur, ou réutiliser:
  • en conséquence, vous n'avez pas besoin du champ

(personnellement, bien que, je rentrais d'un string, mais « meh »)

Comme si:

DynamicAccessor<T> dynAccessor = new DynamicAccessor<T>(); 
    MethodInfo AppendMethod = typeof(StringBuilder).GetMethod("Append", new[] { typeof(Object) }); //Append method of Stringbuilder 

    var method = new DynamicMethod("ClassWriter", typeof(StringBuilder), new[] { typeof(T) }, typeof(T), true); 
    var generator = method.GetILGenerator(); 
    generator.Emit(OpCodes.Newobj, typeof(StringBuilder).GetConstructor(Type.EmptyTypes)); //make our string builder 
    //iterate through all the instance of T's props and sb.Append their values. 
    PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); 
    foreach (var info in props) 
    { 
     generator.Emit(OpCodes.Ldarg_0); 
     generator.Emit(OpCodes.Callvirt, info.GetGetMethod()); //call the Getter 
     if (info.PropertyType.IsValueType) 
     { 
      generator.Emit(OpCodes.Box, info.PropertyType); 
     } 
     generator.Emit(OpCodes.Callvirt, AppendMethod);  //Call Append 
    } 
    generator.Emit(OpCodes.Ret);   //return pointer to sb 

Cela génère l'équivalent de:

StringBuilder ClassWriter(T obj) { 
    return new StringBuilder.Append((object)obj.Foo).Append((object)obj.Bar) 
        .Append((object)obj.Blip).Append((object)obj.Blap); 
} 
+0

+1, très bien expliqué. –

+0

Ah merci, c'est une super explication! Je vois ce que tu veux dire par la boxe, je suis tellement habitué au compilateur à résoudre automatiquement la surcharge correcte à appeler. Je ne suis pas sûr de ce que vous entendez par Append est une API fluide, est-ce que cela signifie que la valeur ajoutée n'est pas consommée par la pile? Et d'où vient Ldarg_0? Désolé pour toutes les questions xD – Josh

+2

par couramment, je veux dire que Append ne retourne pas 'void' - il renvoie' this'; vous appelez '.Append (...). Append (...). Append (...)' etc. Vous laissiez une valeur sur la pile après chaque appel. 'arg0' est le paramètre d'entrée (puisqu'il s'agit d'une méthode statique). Pour une méthode d'instance, 'arg0' est" this ". –

Questions connexes