2009-10-20 16 views
5

Ich versuche Code zu generieren, der einen StringBuilder akzeptiert, und schreibt die Werte aller Eigenschaften in einer Klasse in eine Zeichenfolge. Ich habe Folgendes, aber ich erhalte derzeit ein "Ungültiges Methodentoken" im folgenden Code:Stringbuilder in 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; 
    } 

Irgendwelche Ideen? Vielen Dank im Voraus :)

+0

(Antwort:

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 

Dies das Äquivalent zu erzeugt zu kommentieren) –

Antwort

5

Alle Eigenschaften, die Werttypen sind (int usw.) müssen Boxen; oder Sie müssen eine andere Append Überladung verwenden.

auch:

  • müssen Sie das Objekt jedes Mal (arg0)
  • StringBuilder.Append laden ist ein fließend API; Sie müssen entweder den Wert Pop, oder wieder verwenden:
  • als Folge Sie das Feld

(persönlich, wenn auch nicht brauchen, würde ich eine string zurück, aber „meh“)

wie folgt:

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, sehr schön erklärt. –

+0

Ah danke, das ist eine super Erklärung! Ich sehe, was du mit dem Boxen meinst, ich benutze so den Compiler automatisch die richtige Überladung aufzulösen. Ich bin mir nicht sicher, was Sie meinen, Append ist eine fließende API, heißt das, dass der Wert, der angehängt wird, nicht vom Stapel verbraucht wird? Und woher bekommt Ldarg_0 seinen Input? Sorry für alle Fragen xD – Josh

+2

von fließend, ich meine, dass Append nicht 'void' zurückgibt - es gibt' this' zurück; Sie rufen '.Append (...) auf. Anhängen (...). Anhängen (...)' usw. Sie hinterließen nach jedem Aufruf einen Wert auf dem Stack. 'arg0' ist der Eingabeparameter (da dies eine statische Methode ist). Bei einer Instanzmethode ist "arg0" "this". –