2009-10-20 43 views
5

我想生成一個StringBuilder的代碼,並將一個類中的所有屬性的值寫入字符串。我有以下內容,但我目前在下面的代碼中收到「無效方法令牌」:在CIL(MSIL)中的Stringbuilder

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; 
    } 

有沒有想法? 在此先感謝:)

+0

(回答評論) – 2009-10-20 23:43:57

回答

5

任何屬性值類型(int等)將需要裝箱;或者您將需要使用不同的Append過載。

另外:

  • 需要加載的對象的每個時間(爲arg0)
  • StringBuilder.Append是一個流利API;您可能需要彈出的值,或再使用它:
  • 因此,你不需要現場

(個人,不過,我會返回一個string,但「咩」 )

像這樣:

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 

這將生成等價於:

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,非常好解釋。 – 2009-10-20 20:31:48

+0

啊謝謝,這是一個超級解釋! 我明白你的意思是拳擊,我很習慣編譯器自動解決正確的重載調用。 我不確定Append的意思是流暢的API,這是否意味着被附加的值沒有被從堆棧中消耗? Ldarg_0從哪裏得到它的輸入? 對不起所有的問題xD – Josh 2009-10-20 21:03:35

+2

流利的,我的意思是說,Append不返回'void' - 它返回'this';你可以調用'.Append(...),Append(...),Append(...)'等等。每次調用之後,你都會在堆棧上留下一個值。 'arg0'是輸入參數(因爲這是一個靜態方法)。對於一個實例方法,'arg0'是「this」。 – 2009-10-20 23:43:26