2012-05-01 3 views
12

現在、私はSystem.Reflection.Emitコードの生成に関する問題に取り組んでいます。私はC#でdefault(SomeType)を使用する場所でCILがどのように放射するかを調べようとしています。"default(SomeType)"をC#からCILに変換するにはどうすればいいですか?

私はVisual Studio 11 Betaから基本的な実験をいくつか実行しました。 JustDecompileは私にdefault(bool)default(string)に対して次のCILの出力を示し、そして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に、コンパイラによって解決を取得するようです。

.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 

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

すべての3つの方法

は、同じCILのメソッド本体を生成:


は、私は3つの汎用メソッドを使用して、より一般的な場合に何が起こるか見に行きました

質問:

C#のdefault(SomeType)がCILの&hellipに最も近いと結論づけることはできますか? (string除く?)非プリミティブ型の

  • initobj
  • ldc.iX.0/ldnull /などのプリミティブ型(プラスstring)のために?

そして、なぜCreateClassNull<T>はちょうどldnullに変換されませんが、その代わりinitobjか?結局のところldnullは、(これも参照型です)に対して発行されました。

+0

「プリミティブ」タイプはどういう意味ですか?値型と参照型があります。 \ – sehe

+0

"プリミティブ型"では、CTS(?)でサポートされているものを指しています - 'bool'、' byte'、 'char'、' int '、' float'、 'double'などです。ユーザー定義の値や参照型と異なり、より基本的な型に分解することはできません。 – stakx

+0

私は、コンパイラが次の命令への分岐を削除しなかったので、最適化されていないデバッグを見なければならないことに注意してください。 –

答えて

15

私はC#のdefault(SomeType)は、プリミティブ型のためのCILのinitobjなど非プリミティブ型とldc.i4.0ldnull、ために最も密接に対応するすべての本から結論することはできますか?定数、コンパイル時間は、その後、一定の値が放出されるように、C#コンパイラがdefault(T)を分類する場合:合理的な要約だが、それについて考えるには良い方法がある

。数値型は0、boolはfalse、参照型はnullです。定数として分類されない場合は、(1)一時変数を発行する、(2)一時的なアドレスを取得する、(3)その一時変数をそのアドレスで初期化する、および(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

参照型( 'T:class')に' null 'をロードすると、ボックス化されていない 'T'に関するエラーが発生するのはなぜですか? – Blindy

+0

+1コンパイラがいつコンパイル時定数として 'default(T)'を分類しないのか不思議です。私の推測は「ジェネリック医薬品を扱うとき」ですが、他の症例があるかどうか不思議です。 – dasblinkenlight

+1

@dasblinkenlight: 'default(int?)'はコンパイル時定数ではありません。 'default(Guid)'はコンパイル時定数ではありません。組み込みではない値型は定数ではありません。 –

1

はい、これはdefaultの機能です。あなたは基本的には0(またはそれに相当するもの)の構文砂糖であると推測するのは正しいです。

+0

しかし、コンパイラはどのようにして(つまり、どのようにして)さまざまなエンコーディング方法から選択できますか? 'ldnull'、' ldfalse'、 'ldc。... .0'は明らかに分かりそうですが、なぜ' initobj'ですか? – stakx

+0

簡単に、 'Nullable 'は(値)タイプのオブジェクトなので、 'initobj'で初期化されます。値の型はヌル(Nullable <型)でなくてもかまいませんので、空の(デフォルトのコンストラクタ)オブジェクトとして初期化する必要があります。 – Blindy

+0

そして、 'string'が異なって動作する理由は、それがクラスである* value *型ではないからです。それはヌル参照を保持することができるので、例えば 'default(Form)'のように 'default(string)'は 'null'に解決されます。 – Blindy

関連する問題