5

我试图在运行时将以下代码作为 IL 代码发出。

    class TestObject {
        public int Hello {get;set;}
        public int Test {get;set;}
    }

    static TestObject test(BinaryReader reader) {
        var a = new TestObject();
        a.Hello = reader.ReadInt32();
        a.Test = reader.ReadInt32();
        return a;
    }

LINQPad 显示:

test:
IL_0000:  nop         
IL_0001:  newobj      UserQuery+TestObject..ctor
IL_0006:  stloc.0     // a
IL_0007:  ldloc.0     // a
IL_0008:  ldarg.0     
IL_0009:  callvirt    System.IO.BinaryReader.ReadInt32
IL_000E:  callvirt    UserQuery+TestObject.set_Hello
IL_0013:  nop         
IL_0014:  ldloc.0     // a
IL_0015:  ldarg.0     
IL_0016:  callvirt    System.IO.BinaryReader.ReadInt32
IL_001B:  callvirt    UserQuery+TestObject.set_Test
IL_0020:  nop         
IL_0021:  ldloc.0     // a
IL_0022:  stloc.1     
IL_0023:  br.s        IL_0025
IL_0025:  ldloc.1     
IL_0026:  ret         

尝试用 C# 重现它:

        var method = new DynamicMethod("DynamicCreate", typeof(TestSubject), new Type[] {typeof(BinaryReader)},
            typeof(TestSubject).Module);
        var il = method.GetILGenerator();

        var properties = from property in typeof(TestSubject).GetProperties()
            let orderAttribute =
                property.GetCustomAttributes(typeof(OrderAttribute), false).SingleOrDefault() as OrderAttribute
            orderby orderAttribute.Order
            select property;

        il.Emit(OpCodes.Nop);
        il.Emit(OpCodes.Newobj, typeof(TestSubject).GetConstructors()[0]); // pushes a new instance of testsubject on the stack
        il.Emit(OpCodes.Stloc_0); // pop the instance to local variable 0
        foreach (var prop in properties)
        {
            il.Emit(OpCodes.Ldloc_0); // load local variable 0
            il.Emit(OpCodes.Ldarg_0); // load argument 0 (the binary reader)
            il.Emit(OpCodes.Callvirt, typeof(BinaryReader).GetMethod("ReadInt32", BindingFlags.Public | BindingFlags.Instance)); // call the binary reader method (ReadInt32)
            il.Emit(OpCodes.Callvirt, prop.SetMethod); // call the setter of the property (instance of local variable 0 and return value of readint32)
            il.Emit(OpCodes.Nop);
        }
        il.Emit(OpCodes.Ldloc_0);
        il.Emit(OpCodes.Stloc_1);
        var label = il.DefineLabel();
        il.Emit(OpCodes.Br_S, label);
        il.MarkLabel(label);
        il.Emit(OpCodes.Ldloc_1); // push the test subject instance
        il.Emit(OpCodes.Ret); // and return

        var generator = (Load)method.CreateDelegate(typeof(Load));
        var reader = new BinaryReader(new MemoryStream(new byte[] {1, 2, 3, 4, 0, 0, 0, 1}));
        var test = generator(reader); // exception here

TestSubject 类:

public class TestSubject
{

    [Order]
    public int Test1 { get; set; }

    [Order]
    public int Test2 { get; set; }

}

给我以下例外:

System.InvalidProgramException {"Die Common Language Runtime hat ein ungültiges Programm gefunden."}

那有什么问题?

4

2 回答 2

4

您需要在使用本地变量之前声明它们。此外,可以简化代码(IL 是在调试版本中生成的)。

il.DeclareLocal(typeof(TestSubject));

il.Emit(OpCodes.Newobj, typeof(TestSubject).GetConstructors()[0]); // pushes a new instance of testsubject on the stack
il.Emit(OpCodes.Stloc_0); // store the instance in local variable 0
foreach (var prop in properties)
{
    il.Emit(OpCodes.Ldloc_0); // load local variable 0
    il.Emit(OpCodes.Ldarg_0); // load argument 0 (the binary reader)
    il.Emit(OpCodes.Callvirt, typeof(BinaryReader).GetMethod("ReadInt32", BindingFlags.Public | BindingFlags.Instance)); // call the binary reader method (ReadInt32)
    il.Emit(OpCodes.Callvirt, prop.SetMethod); // call the setter of the property (instance of local variable 0 and return value of readint32)
}
il.Emit(OpCodes.Ldloc_0); // push the test subject instance
il.Emit(OpCodes.Ret); // and return
于 2016-07-31T15:12:45.963 回答
1

你没有定义本地人。

此外,这里的 IL 来自调试模式输出。这比必要的要复杂一些。

反射发射几乎已经过时(在某些情况下仍然需要)。使用表达式树生成这样的代码要容易得多。删除所有这些代码并将其替换为表达式树。

于 2016-07-31T15:04:03.513 回答