Струнный строитель в 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;
}
Есть идеи? Заранее спасибо:)
1 ответ
Решение
Любые свойства, которые являются типами значений (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);
}