不使用语法糖将 IL 转换为 C#

Converting IL to C# with no syntactic sugar

我正在寻找一个程序,它会向我显示最低级别(即没有语法糖)的 C# 代码给定 IL 代码。

我尝试使用 .NET Reflector 查看一个 .exe 文件,该文件包含一个带有 foreach 循环的简单控制台应用程序,希望看到 GetEnumerator()MoveNext()Current 等,但是它显示为 foreach 循环。

有这样的程序吗?或者是否可以在 .NET Reflector 中 select "no syntactic sugar"?

ILSpy 的当前版本有一组相当大的 enabling/disabling 反编译器转换功能选项:

...
        static void Main(string[] args) {
            foreach (var arg in args) {
                Console.WriteLine(arg);
            }
        }
...

如果需要,您可以通过剥离 ICSharpCode.Decompiler.IL.Transforms.*ICSharpCode.Decompiler.CSharp.StatementBuilder 中的逻辑更进一步;也许打开一个问题询问,是否会感谢您的更改的 PR,因为这些 "rawness" 设置中的大多数都是最近才添加的。


枚举器的更好示例

一段简洁的代码

var numbers = new List<int> { 0, 1, 2 };
foreach (var num in numbers) Console.WriteLine(num);

编译为

System.Collections.Generic.List<int> list = new System.Collections.Generic.List<int>();
list.Add(0);
list.Add(1);
list.Add(2);
System.Collections.Generic.List<int> numbers = list;
System.Collections.Generic.List<int>.Enumerator enumerator = numbers.GetEnumerator();
try
{
    while (enumerator.MoveNext())
    {
        int num = enumerator.Current;
        System.Console.WriteLine(num);
    }
}
finally
{
    ((System.IDisposable)enumerator).Dispose();
}

(禁用所有转换设置后看到的)


进一步了解 for 循环:

就编译而言,for (a; b; c) da; while (b) { d; c; } 相同(除了放置 continue-label),因此反编译器会自行决定它可能是哪种循环基于 init-statement、condition 和 post-statement 之间的上下文相似性,因此您甚至可以手动编写代码

var a = 0;
while (a < args.Length) {
    Console.WriteLine(args[a]);
    a++;
}

这将被检测为 for 循环(因为在 IL 中没有说明)

for (int a = 0; a < args.Length; a++)
{
    System.Console.WriteLine(args[a]);
}

上对 YellowAfterlife OP 说:

In your screenshot, it's showing a for loop. But behind the scenes a for loop is not used for a foreach, right? It uses a while loop.

迭代数组时不使用枚举器对象。它使用整数索引计数器变量代替。 你知道,就像一个 for 循环。 IL 使用 OpCodes.Br_SOpCodes.Blt_S,我们可以说它们是 "goto"。当然,如果你坚持的话,你可以把它写成一个 while 循环。

为了测试,我写了这段代码:

static void Main(string[] args)
{
    var index = 0;
    while (index < args.Length)
    {
        var arg = args[index];
        Console.WriteLine(arg);
        index++;
    }
}

这是 ILSpy 的输出:

private static void Main(string[] args)
{
    for (int index = 0; index < args.Length; index++)
    {
        Console.WriteLine(args[index]);
    }
}

事实上,在 IL 中,检查被移到循环之后,并在开始时跳转到它。请记住 while 循环(不同于 do ... while 循环)应该在之前进行检查。查看IL:

// for (int i = 0; i < args.Length; i++)
IL_0000: ldc.i4.0
IL_0001: stloc.0
// (no C# code)
IL_0002: br.s IL_0010
// loop start (head: IL_0010)
    // Console.WriteLine(args[i]);
    IL_0004: ldarg.0
    IL_0005: ldloc.0
    IL_0006: ldelem.ref
    IL_0007: call void [mscorlib]System.Console::WriteLine(string)
    // for (int i = 0; i < args.Length; i++)
    IL_000c: ldloc.0
    IL_000d: ldc.i4.1
    IL_000e: add
    IL_000f: stloc.0

    // for (int i = 0; i < args.Length; i++)
    IL_0010: ldloc.0
    IL_0011: ldarg.0
    IL_0012: ldlen
    IL_0013: conv.i4
    IL_0014: blt.s IL_0004
// end loop

// (no C# code)
IL_0016: ret

注意ldlen获取数组的长度。

你可以验证this code on ShatpLab.

编译器正在优化对数组的访问。因此,我们可以争辩说编译器将我的 while 循环变成了 for 循环。