2009-10-20 11 views
5

Estoy intentando generar código que toma un StringBuilder y escribe los valores de todas las propiedades de una clase en una cadena. Tengo la siguiente, pero actualmente me estoy poniendo un "token de método no válido" en el código siguiente:Stringbuilder en 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; 
    } 

¿Alguna idea? Gracias de antemano :)

+0

(respondió para comentar) –

Respuesta

5

Cualquier propiedad que sea de valor (int etc.) necesitará boxeo; o tendrá que usar una sobrecarga diferente Append.

también:

  • tiene que cargar el objeto cada vez (arg0)
  • StringBuilder.Append es una API fluida; o hay que hacer estallar el valor, o volver a utilizarlo:
  • , como consecuencia, no es necesario el campo

(personalmente, sin embargo, me gustaría volver una string, pero "era normalito")

así:

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 

Esto genera el equivalente a:

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, muy bien explicado. –

+0

¡Ah, gracias, esta es una gran explicación! Veo lo que quiere decir con el boxeo, estoy acostumbrado a que el compilador resuelva automáticamente la sobrecarga correcta para llamar. No estoy seguro de lo que quiere decir con Append es una API fluida, ¿eso significa que el valor que se adjunta no se está consumiendo desde la pila? ¿Y de dónde saca Ldarg_0 su contribución? Disculpe por todas las preguntas xD – Josh

+2

por fluido, me refiero a que Append no devuelve 'void' - devuelve' this'; usted llama a '.Append (...). Append (...). Append (...)' etc. Estaba dejando un valor en la pila después de cada llamada. 'arg0' es el parámetro de entrada (ya que este es un método estático). Para un método de instancia, 'arg0' es" this ". –