【问题标题】:See what code gets pre-compiled by the C# compiler查看 C# 编译器预编译了哪些代码
【发布时间】:2016-03-14 05:36:19
【问题描述】:

我如何才能看到 C#“语法糖”在幕后的真正作用? 比如我知道C#lock语句被预编译为:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

而且我知道,如果您直接在类内部(而不是在构造函数中)声明和初始化实例字段,那么在类中声明这些字段并在构造函数中初始化它们就是语法糖。

我的问题是,如果我要使用这些语法糖来编写代码,我如何才能看到生成的 C# 代码是什么?

是否有一个“预编译”过程,首先将这些语法糖转换为更复杂的 C# 代码,然后将其编译成 CIL?

【问题讨论】:

标签: c#


【解决方案1】:

没有生成语法糖结构的 C# 代码的过程,代码最终编译为 IL(可能在中间有抽象语法树)。

您可以使用任何 .Net 反编译器查看结果 - ILDasm 作为框架的一部分,或其他工具,如 LinqPadILSpy

即LinqPad IL 查看器显示的编译示例的结果:

IL_0000:  nop         
IL_0001:  newobj      System.Object..ctor
IL_0006:  stloc.0     // temp
IL_0007:  ldloc.0     // temp
IL_0008:  call        System.Threading.Monitor.Enter
IL_000D:  nop         
IL_000E:  nop         
IL_000F:  nop         
IL_0010:  leave.s     IL_001C
IL_0012:  nop         
IL_0013:  ldloc.0     // temp
IL_0014:  call        System.Threading.Monitor.Exit
IL_0019:  nop         
IL_001A:  nop         
IL_001B:  endfinally  
IL_001C:  nop         
IL_001D:  ret   

类似于输出

var temp = new object();
lock(temp)
{

}

IL:

IL_0000:  nop         
IL_0001:  newobj      System.Object..ctor
IL_0006:  stloc.0     // temp
IL_0007:  ldc.i4.0    
IL_0008:  stloc.1     // <>s__LockTaken0
IL_0009:  ldloc.0     // temp
IL_000A:  dup         
IL_000B:  stloc.2     // CS$2$0000
IL_000C:  ldloca.s    01 // <>s__LockTaken0
IL_000E:  call        System.Threading.Monitor.Enter
IL_0013:  nop         
IL_0014:  nop         
IL_0015:  nop         
IL_0016:  leave.s     IL_0028
IL_0018:  ldloc.1     // <>s__LockTaken0
IL_0019:  ldc.i4.0    
IL_001A:  ceq         
IL_001C:  stloc.3     // CS$4$0001
IL_001D:  ldloc.3     // CS$4$0001
IL_001E:  brtrue.s    IL_0027
IL_0020:  ldloc.2     // CS$2$0000
IL_0021:  call        System.Threading.Monitor.Exit
IL_0026:  nop         
IL_0027:  endfinally  
IL_0028:  nop         
IL_0029:  ret  

请注意,对于每个“语法糖”,都有“...内部”的帖子/和文章准确描述了正在发生的事情(因为有时阅读 C# 规范可能很困难)。 IE。 What really happens in a try { return x; } finally { x = null; } statement?

【讨论】:

  • 哦,我明白了,当人们说它是 的语法糖时,他们基本上只是将实际的 CIL 输出转换为 C#。
  • 几年前,当通过反射器等反编译器打开程序集时,语法糖被扩展为“丑陋的代码”。但是现在反编译器越来越聪明,可以完美还原语法糖。
  • +10000000 用于 linqpad 链接。我最喜欢的工具之一。
  • @Backwards_Dave 所有“语法糖”结构都在 C# 规范中明确定义(例如 {get;set} 等价于什么或应该如何实现 lock)。确实有些人可以查看 IL 并弄清楚事情,但我怀疑有人会这样做(除了需要将 IL 转换为可读 C# 的反编译器作者)
  • 如果某些反编译器有一个标志可以让它们显示降低的结果(即“丑陋的代码”),我会很高兴。
【解决方案2】:

我知道C# lock 语句被预编译为...

不,你不知道,因为那是错误的。首先,因为这不再是为锁生成的代码,而且自 2009 年以来一直没有,其次,因为 C# 规范没有说需要编译器来生成和编译该代码。它表示编译器需要生成与给定代码语义等效的代码。

我的问题是,如果我要使用这些语法糖来编写代码,我如何才能看到生成的 C# 代码是什么?

我编写了很多进行这些转换的语义分析器。没有这样的“生成”代码。在语义分析过程运行时,编译器无法在文本级别上工作。它适用于表示代码的内部数据结构,并对其进行转换。

是否有一个“预编译”过程,首先将这些语法糖转换为更复杂的 C# 代码,然后将其编译成 CIL?

不在您感兴趣的级别,不。

我如何才能看到 C#“语法糖”在幕后的真正作用?

从 github 获取 Roslyn 源代码并仔细检查其中包含“降低”一词的任何内容。这就是你感兴趣的魔法发生的地方。 (“降低”意味着从可空整数加法等高级构造开始,并将其重写为一系列低级操作,如对 HasValue 的调用等。)我可能建议您特别关注可空算术降低,用户定义的转换降低和 LINQ 表达式降低,因为这些降低通道有许多有趣的问题需要解决。

您可能还对我写的有关相关主题的文章感兴趣:http://ericlippert.com/2014/04/28/lowering-in-language-design-part-one/

【讨论】:

    猜你喜欢
    • 2010-10-02
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-16
    相关资源
    最近更新 更多