2009-10-20 21 views
5

Próbuję wygenerować kod, który pobiera StringBuilder, i zapisuje wartości wszystkich właściwości w klasie do ciągu znaków. Mam następujące elementy, ale obecnie otrzymuję komunikat "Nieprawidłowy token metody" w następującym kodzie:Stringbuilder w 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; 
    } 

Jakieś pomysły? Z góry dziękuję :)

+0

(odpowiedział skomentować) –

Odpowiedz

5

Wszelkie właściwości, które są typami wartości (int itp.) Będą wymagały boksowania; lub będziesz musiał użyć innego przeciążenia Append.

również:

  • trzeba załadować obiekt za każdym razem (arg0)
  • StringBuilder.Append jest biegle API; albo trzeba pop wartość lub ponownie wykorzystać go:
  • w konsekwencji, nie trzeba boiska

(osobiście, choć bym zwrócić string, ale „meh”)

tak:

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 

ten generuje równoważny:

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, bardzo ładnie wyjaśniony. –

+0

Ah dzięki, to jest super wyjaśnienie! Widzę, co masz na myśli przez boks, jestem tak przyzwyczajony, że kompilator automatycznie rozwiązuje odpowiednie przeciążenie, aby zadzwonić. Nie jestem pewien, co masz na myśli, mówiąc o Append, to płynne API, to znaczy, że wartość, która jest dołączana, nie jest zużywana ze stosu? A skąd Ldarg_0 pobiera dane wejściowe? Przepraszamy za wszystkie pytania xD – Josh

+2

płynnie, mam na myśli to, że Append nie zwraca 'void' - zwraca' this'; Wywołujesz '.Append (...) .Dodaj (...) .Dodaj (...)' itd. Zostawiłeś wartość na stosie po każdym wywołaniu. "arg0" jest parametrem wejściowym (ponieważ jest to metoda statyczna). Dla metody instancji "arg0" jest "to". –