3

How can I see what C# "syntactic sugar" is really doing behind the scenes? For example, I know that the C# lock statement is pre-compiled to:

var temp = obj;

Monitor.Enter(temp);

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

And I know that if you declare and initialize instance fields directly inside the class (not in the constructor), it's syntactic sugar for declaring those fields in the class and initializing them in the constructor.

My question is, if I were to write code using these syntactic sugars, how can I see what the generated C# code is?

Is there a "precompilation" process that first converts these syntactic sugars to the more complicated C# code, and then it gets compiled into CIL?

C. Augusto Proiete
  • 24,684
  • 2
  • 63
  • 91
David Klempfner
  • 8,700
  • 20
  • 73
  • 153
  • 2
    That's not actually what the lock statement generates these days. See http://ericlippert.com/2009/03/06/locks-and-exceptions-do-not-mix/ – Eric Lippert Dec 09 '15 at 04:18

3 Answers3

13

I know that the C# lock statement is pre-compiled to...

No, you don't know that, because that is false. First, because that is no longer the code generated for locks and has not been since 2009, and second, because the C# specification does not say that a compiler is required to generate that code and compile it. It says that the compiler is required to generate code that is semantically equivalent to the given code.

My question is, if I were to write code using these syntactic sugars, how can I see what the generated C# code is?

I wrote much of the semantic analyzer that does those transformations. There is no such "generated" code. The compiler does not work on the level of text by the time the semantic analysis pass is running. It works on internal data structures that represent the code, and it transforms those.

Is there a "precompilation" process that first converts these syntactic sugars to the more complicated C# code, and then it gets compiled into CIL?

Not at the level you're interested in, no.

How can I see what C# "syntactic sugar" is really doing behind the scenes?

Get the Roslyn source code from github and carefully examine anything that has the word "lowering" in it. That's where the magic you are interested in happens. ("Lowering" means going from a high-level construct like a nullable integer addition and rewriting it into a series of low-level manipulations, like calls to HasValue, and so on.) I might suggest that you particularly look at nullable arithmetic lowering, user-defined conversion lowering and LINQ expression lowering, as these lowering passes have a number of interesting problems to solve.

You might also be interested in an article I wrote on a related topic: http://ericlippert.com/2014/04/28/lowering-in-language-design-part-one/

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
4

There is no process that produces C# code of syntactic sugar constructs, code eventually compile to IL (likely with Abstract Syntax Tree in between).

You can see results with any .Net decompiler - ILDasm as part of framework, or other tools like LinqPad or ILSpy.

I.e. result of compiling your sample shown by LinqPad IL viewer:

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   

Which is similar to output for

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  

Note that for every "syntactic sugar" there is "inside of ..." posts/and articles which describe exactly what is happening (as reading C# specification may be hard sometimes). I.e. What really happens in a try { return x; } finally { x = null; } statement?

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • oh I see so basically when people say that it's syntactic sugar for they are basically just converting what the actual CIL output is, into C#. – David Klempfner Dec 09 '15 at 04:07
  • 2
    A few years ago when opening assemblies via decompilers like reflector, syntax sugars were expanded into "ugly code". But nowadays the decompilers become more and more wiser, they can restore the syntax sugars perfectly. – Cheng Chen Dec 09 '15 at 04:11
  • +10000000 for linqpad link. One of my favorite tools. – Cubicle.Jockey Dec 09 '15 at 04:13
  • 1
    @Backwards_Dave all "syntactic sugar" constructs are explicitly defined in C# specification (like what `{get;set}` is equivalent to or how `lock` should be implemented). Indeed some people can look at IL and figure things out, but I doubt anyone does it (except of authors of decompilers that need to convert IL into readable C#) – Alexei Levenkov Dec 09 '15 at 04:13
  • I would love if some decompiler had a flag that would cause them to show the result of the lowering (that is the "ugly code"). – Ladi Mar 07 '20 at 18:37
2

This question is on top in Google with "c# precompiled code viewer" prompt, so I'll leave here a solution that helped me a lot: https://sharplab.io/. You can view precompiled C# code without "syntactic sugar" with settings: code - C#; platfrom - default; results - C#.

Specter
  • 21
  • 1