How to translate "default(SomeType)" from C# to CIL?

c#, cil, default

Solution

Can I conclude from all this that C#'s `default(SomeType)` corresponds most closely to CIL's `initobj` for non-primitive types and `ldc.i4.0`, `ldnull`, etc. for primitive types?

That's a reasonable summary but a better way to think about it is: if the C# compiler would classify `default(T)` as a compile time constant then the value of the constant is emitted. That is zero for numeric types, false for bool, and null for any reference type. If it would not be classified as a constant then we must (1) emit a temporary variable, (2) obtain the address of the temporary, (3) initobj that temporary variable via its address and (4) ensure that the temporary's value is on the stack when it is needed.

why does `CreateClassNull<T>` not just translate to ldnull, but to initobj instead?

Well, let's do it your way and see what happens:

... etc
.class private auto ansi beforefieldinit P
       extends [mscorlib]System.Object
{
  .method private hidebysig static !!T  M<class T>() cil managed
  {
    .maxstack  1
    ldnull
    ret
  } 
  ... etc

...

D:\>peverify foo.exe

Microsoft (R) .NET Framework PE Verifier.  Version  4.0.30319.17379
Copyright (c) Microsoft Corporation.  All rights reserved.

[IL]: Error: 
[d:\foo.exe : P::M[T]]
[offset 0x00000001]
[found Nullobjref 'NullReference']     
[expected (unboxed) 'T'] 
Unexpected type on the stack.
1 Error(s) Verifying d:\foo.exe

That would probably be why we don't do that.

Problem

I'm currently working on a problem that involves `System.Reflection.Emit` code generation. I'm trying to figure out what CIL to emit in places where I would use `default(SomeType)` in C#. I've run a few basic experiments from Visual Studio 11 Beta. JustDecompile shows me the following CIL output for `default(bool)`, `default(string)`, and `default(int?`: ``` .locals init ( [0] bool V_0, [1] string V_1, [2] valuetype [mscorlib]System.Nullable`1<int32> V_2 ) // bool b = default(bool); ldc.i4.0 stloc.0 // string s = default(string); ldnull stloc.1 // int? ni = default(int?); ldloca.s V_2 initobj valuetype [mscorlib]System.Nullable`1<int32> ``` Judging from this, `default(T)` seems to get resolved by the compiler to the most appropriate CIL for the given types. I went on to see what would happen in the more general case, using three generic methods: ``` T CreateStructDefault<T>() where T : struct { return default(T); } T CreateClassDefault<T>() where T : class { return default(T); } T CreateClassNull<T>() where T : class { return null; } ``` All three methods produce the same CIL method body: ``` .locals init ( [0] !!T V_0, [1] !!T V_1 ) IL_0000: nop IL_0001: ldloca.s V_1 IL_0003: initobj !!T IL_0009: ldloc.1 IL_000a: stloc.0 IL_000b: br.s IL_000d IL_000d: ldloc.0 IL_000e: ret ``` Question: Can I conclude from all this that C#'s `default(SomeType)` corresponds most closely to CIL's… - `initobj` for non-primitive types (except `string`?) - `ldc.iX.0` / `ldnull` / etc. for primitive types (plus `string`)? And why does `CreateClassNull<T>` not just translate to `ldnull`, but to `initobj` instead? After all, `ldnull` was emitted for `string` (which is also a reference type).

Original source