Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

default

cil

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).

like image 730
stakx - no longer contributing Avatar asked May 01 '12 15:05

stakx - no longer contributing


2 Answers

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.

like image 70
Eric Lippert Avatar answered Nov 05 '22 07:11

Eric Lippert


Yes, that's what default does. You are correct in deducing it's just syntactic sugar for basically 0 (or equivalents).

like image 1
Blindy Avatar answered Nov 05 '22 08:11

Blindy