【问题标题】:InvalidProgramException (Invalid IL Code)?InvalidProgramException(无效的 IL 代码)?
【发布时间】:2016-07-31 14:59:08
【问题描述】:

我试图在运行时将以下代码作为 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。"}

这有什么问题?

【问题讨论】:

    标签: c# intermediate-language


    【解决方案1】:

    您需要在使用本地变量之前声明它们。此外,代码可以简化(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
    

    【讨论】:

    • 这似乎对我有用。但正如@usr 提到的,我现在可以使用表达式树了。我喜欢它。这个问题对我有很好的学习效果。
    【解决方案2】:

    你没有定义本地人。

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

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

    【讨论】:

      猜你喜欢
      • 2017-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-20
      • 1970-01-01
      • 1970-01-01
      • 2018-11-05
      • 1970-01-01
      相关资源
      最近更新 更多