Подтвердить что ты не робот

Как перевести "default (SomeType)" с С# на CIL?

В настоящее время я работаю над проблемой, связанной с генерированием кода System.Reflection.Emit. Я пытаюсь понять, что CIL испускает в местах, где я бы использовал default(SomeType) в С#.

Я провел несколько базовых экспериментов из бета-версии Visual Studio 11. JustDecompile показывает мне следующий вывод CIL для default(bool), default(string) и 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>

Судя по этому, default(T), похоже, будет разрешен компилятором для наиболее подходящего CIL для данных типов.


Я продолжал видеть, что произойдет в более общем случае, используя три общих метода:

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;       }

Все три метода производят одно и то же тело метода CIL:

.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

Вопрос:

Могу ли я заключить из всего этого, что С# default(SomeType) наиболее близко соответствует CIL's & hellip;

  • initobj для не-примитивных типов (кроме string?)
  • ldc.iX.0/ldnull/и т.д. для примитивных типов (плюс string)?

И почему CreateClassNull<T> не просто переводит на ldnull, а на initobj вместо этого? В конце концов, ldnull испущен для string (который также является ссылочным типом).

4b9b3361

Ответ 1

Могу ли я заключить из всего этого, что С# default(SomeType) наиболее близко соответствует CIL initobj для не-примитивных типов и ldc.i4.0, ldnull и т.д. для примитивных типов?

Это разумное резюме, но лучший способ подумать об этом: если компилятор С# будет классифицировать default(T) как константу времени компиляции, тогда испускается значение константы. Это значение равно нулю для числовых типов, false для bool и null для любого ссылочного типа. Если он не будет классифицирован как константа, тогда мы должны (1) испускать временную переменную, (2) получить адрес временной, (3) initobj этой временной переменной через свой адрес и (4) гарантировать, что временное значение в стеке, когда это необходимо.

почему CreateClassNull<T> не просто перевести на ldnull, а вместо initobj?

Хорошо, сделай это по-своему и посмотри, что получится:

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

Вероятно, поэтому мы этого не делаем.

Ответ 2

Да, что делает default. Вы правильно вывели его только для синтаксического сахара в основном 0 (или эквивалентов).