Skip to main content
 首页 » 编程设计

c#之IL 程序无效,我做错了什么 (简单的 if-else 代码)

2024年12月31日15lori

我正在学习 IL。我发现 LINQpad 实际上非常适合编写 C# 代码并立即查看生成的 IL。比 VS/ILSpy 快得多。

我写了这个简单的代码:

int x = 10; 
int y = 20; 
 
if (x > y) 
    Console.WriteLine("X is greater"); 
else 
    Console.WriteLine("y is greater"); 

得到:

IL_0001:  ldc.i4.s    0A  
IL_0003:  stloc.0     // x 
IL_0004:  ldc.i4.s    14  
IL_0006:  stloc.1     // y 
IL_0007:  ldloc.0     // x 
IL_0008:  ldloc.1     // y 
IL_0009:  cgt          
IL_000B:  ldc.i4.0     
IL_000C:  ceq          
IL_000E:  stloc.2     // CS$4$0000 
IL_000F:  ldloc.2     // CS$4$0000 
IL_0010:  brtrue.s    IL_001F 
IL_0012:  ldstr       "X is greater" 
IL_0017:  call        System.Console.WriteLine 
IL_001C:  nop          
IL_001D:  br.s        IL_002A 
IL_001F:  ldstr       "y is greater" 
IL_0024:  call        System.Console.WriteLine 

有一件事我不太明白,并且觉得多余的是 brtrue.s 之前的 STLoc.2ldloc.2 -为什么我们需要存储和加载从 ceq 返回的值,因为它已经由 ceq 推送到堆栈中?

所以我自己尝试了一下,没有执行这两个命令:

static void Main() 
{ 
    var asm_name = new AssemblyName("Asm"); 
    var asm_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(asm_name, AssemblyBuilderAccess.RunAndSave); 
    var mod_builder = asm_builder.DefineDynamicModule("Mod", "Test.dll"); 
    var type_builder = mod_builder.DefineType("Test", TypeAttributes.Public); 
    var cmp_builder = type_builder.DefineMethod("Compare", 
        MethodAttributes.Public | MethodAttributes.HideBySig, 
        CallingConventions.Standard, 
        typeof(void), 
        Type.EmptyTypes); 
 
    var WL = typeof(Console).GetMethod("WriteLine", 
        BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string) }, null); 
 
    // int x = 10; 
    // int y = 20; 
    // if (x > y) 
    //    WL("x is greater"); 
    // else 
    //    WL("y is greater"); 
 
    var il = cmp_builder.GetILGenerator(); 
    var ygtX = il.DefineLabel();                // defines a label for y > x 
    il.Emit(OpCodes.Ldc_I4, 0xA);               // push 10 
    il.Emit(OpCodes.Stloc_0);                   // pop 10 to location 0 (x) 
    il.Emit(OpCodes.Ldc_I4, 0x14);              // push 20 
    il.Emit(OpCodes.Stloc_1);                   // pop 20 to location 1 (y) 
    il.Emit(OpCodes.Ldloc_0);                   // push x 
    il.Emit(OpCodes.Ldloc_1);                   // push y 
    il.Emit(OpCodes.Cgt);                       // pops x, y and compares them. pushes 1 if x > y else 0 
    il.Emit(OpCodes.Ldc_I4_0);                  // push 0 
    il.Emit(OpCodes.Ceq);                       // pops the last two values, pushes 1 if equal else 0 
    il.Emit(OpCodes.Brtrue_S, ygtX);            // pops the last value, branches to ygt label if value is true (== 1) 
    il.Emit(OpCodes.Ldstr, "x is greater");     // pushes string 
    il.Emit(OpCodes.Call, WL);                  // pops string, calls WriteLine with it 
    il.MarkLabel(ygtX);                         // marks the beginning of y > x label 
    il.Emit(OpCodes.Ldstr, "y is greater");     // pushes string 
    il.Emit(OpCodes.Call, WL);                  // pops string, calls WriteLine with it 
    il.Emit(OpCodes.Ret); 
 
    var type = type_builder.CreateType(); 
    var print = type.GetMethod("Compare"); 
    var cmp = (Action)Delegate.CreateDelegate(typeof(Action), null, print); 
    cmp(); 
 
    asm_builder.Save("Test.dll"); 
} 

运行抛出:

Unhandled Exception: System.InvalidProgramException: Common Language Runtime detected an invalid program. 

添加这两条指令(如第一个 IL 代码片段中所示的分支之前的存储和加载)没有帮助。

我做错了什么?

感谢您的帮助。

编辑:这是 LINQpad 的优化输出。更有意义。这两个命令确实是多余的。

IL_0000:  ldc.i4.s    0A  
IL_0002:  stloc.0     // x 
IL_0003:  ldc.i4.s    14  
IL_0005:  stloc.1     // y 
IL_0006:  ldloc.0     // x 
IL_0007:  ldloc.1     // y 
IL_0008:  ble.s       IL_0015 
IL_000A:  ldstr       "X is greater" 
IL_000F:  call        System.Console.WriteLine 
IL_0014:  ret          
IL_0015:  ldstr       "y is greater" 
IL_001A:  call        System.Console.WriteLine 

请您参考如下方法:

我想你需要调用DeclareLocal()在使用局部变量之前先声明它们。