C# .NET反编译器之间的区别;使用;及;“最后试试……”;

C# .NET反编译器之间的区别;使用;及;“最后试试……”;,c#,.net,cil,decompiler,C#,.net,Cil,Decompiler,给定以下C#代码,其中Dispose方法以两种不同的方式调用: class Disposable : IDisposable { public void Dispose() { } } class Program { static void Main(string[] args) { using (var disposable1 = new Disposable()) { Console.Write

给定以下C#代码,其中Dispose方法以两种不同的方式调用:

class Disposable : IDisposable
{
    public void Dispose()
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var disposable1 = new Disposable())
        {
            Console.WriteLine("using");
        }

        var disposable2 = new Disposable();
        try
        {
            Console.WriteLine("try");
        }
        finally
        {
            if (disposable2 != null)
                ((IDisposable)disposable2).Dispose();
        }
    }
}
一旦使用发行版配置进行编译,然后使用ildasm进行反汇编,MSIL如下所示:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       57 (0x39)
  .maxstack  1
  .locals init ([0] class ConsoleApplication9.Disposable disposable2,
           [1] class ConsoleApplication9.Disposable disposable1)
  IL_0000:  newobj     instance void ConsoleApplication9.Disposable::.ctor()
  IL_0005:  stloc.1
  .try
  {
    IL_0006:  ldstr      "using"
    IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0010:  leave.s    IL_001c
  }  // end .try
  finally
  {
    IL_0012:  ldloc.1
    IL_0013:  brfalse.s  IL_001b
    IL_0015:  ldloc.1
    IL_0016:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_001b:  endfinally
  }  // end handler
  IL_001c:  newobj     instance void ConsoleApplication9.Disposable::.ctor()
  IL_0021:  stloc.0
  .try
  {
    IL_0022:  ldstr      "try"
    IL_0027:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_002c:  leave.s    IL_0038
  }  // end .try
  finally
  {
    IL_002e:  ldloc.0
    IL_002f:  brfalse.s  IL_0037
    IL_0031:  ldloc.0
    IL_0032:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
    IL_0037:  endfinally
  }  // end handler
  IL_0038:  ret
} // end of method Program::Main

NET反编译器(如DotPeek或JustDecompile)如何在使用和尝试之间产生区别?最后?

实际上没有区别。正如Mark在评论中所说,如果您编写的代码与编译器使用为生成的代码相同,反编译器将无法发挥作用

但是,许多反编译器(包括DotPeek)实际上可以使用调试符号(.pdb)文件来定位实际的源代码,然后使用实际的源代码,因此根本不会发生反编译。此外,在调试模式下编译也可能会影响模式(也就是说,您尝试使用语句模拟
,在调试和发布编译中可能会产生不同的IL)

要防止DotPeek使用真实的源代码文件,请转到“工具”>“选项”>“反编译器”,并取消选中“在可用时使用来自符号文件的源代码”。然后在发行版中编译代码,并观察DotPeek将使用
将这两条语句反编译为

NET反编译器(如DotPeek或JustDecompile)如何区分使用和尝试…最终

反编译器主要处理模式匹配。通常,IL被翻译成目标语言中可能的最简单等效表示形式(在本例中为C#)。然后,该代码模型将通过一系列转换,这些转换尝试将代码序列与已知模式匹配起来。通过ILSpy的调试构建,您可以实际查看此管道不同阶段的输出

反编译器的管道可能包括像循环重写器那样的转换。循环重写器可以通过查找
while
循环来为
循环重构
,而
循环前面有变量初始值设定项,并且在每个后缘之前也包含公共迭代语句。当检测到这样一个循环时,它会被重写为一个更简洁的
for
循环。它不知道原始代码实际上包含一个
for
循环;它只是试图找到最简洁的方式来表示代码,同时保持正确性


以类似的方式,使用重写器的
将查找
try/finally
块,其中
finally
包含一个简单的空检查和
Dispose()
调用,然后使用
块将它们重写为
,反编译器不知道代码中包含使用
块的
,但由于几乎没有人使用显式的
try/finally
形式,结果往往与原始源代码一致。

它根据模式进行猜测,坦白地说:)如果您手动编写的东西恰好编译成编译器生成的版本将编译成的样子,那么它会猜错。通常不会有这种风险,因为许多编译器扩展涉及不可命名的名称(在常规C#中不合法的名称)或不存在的关键字(例如,反编译器经常显示为
memberof
,可以用IL表示,但不能用C#表示,因此它看到:它知道你不是手工编写的)这就是我的想法,但我对它进行了测试,正如你所看到的,虽然两者在MSIL中看起来完全相同,但例如DotPeek,仍然能够发挥作用……你确定它没有访问PDB的权限吗?你是对的,它使用的不仅仅是程序集本身。这很公平,但在大多数情况下,它实际上并不是反编译:)