|
■No84240 (Tomo さん) に返信
> newをする前は各種フィールドにどのような
> 値が格納されているのでしょうか?
> nullでもない、何か別の値が格納されていたりするのでしょうか?
ECMA-335 を斜め読みしてみましたが、
不確定値になりえるのはローカル変数ぐらいのようですね。
フィールド変数の場合は、default(T) で初期化されるか、
初期値が書き込まれるかのいずれかです。
そのローカル変数についても、C# のコンパイラは init フラグを付与するため、
リフレクション等で動的生成でもしない限り、基本的にはゼロが書き込まれた状態になります。
フィールド変数については、たとえば
public int i = 123;
というインスタンスフィールドだとしても、123 がセットされる前に
0 がセットされた状態で受け渡されるようです。これは newobj 命令(0x73)の仕様だそうで。
構造体に対して用いられる initobj 命令(0xfe 0x15)も同様。
static int などの静的フィールドの場合は、そのクラスに静的コンストラクタが
明示的に実装されているか、それとも beforefieldinit フラグによるものかで
初期化のタイミングが異なる程度で、これもやはりゼロが書き込まれた状態になるようです。
一応、こんな感じの IL を書いて試してみました。
.assembly extern 'mscorlib' {}
.assembly 'Orator' {}
// === Sample クラスの宣言 ===
// このクラスには static なメンバーが無いため、
// BeforeFieldInit フラグ(0x00100000)の有無は特に意味を持ちません
.class public sequential autochar /* beforefieldinit */ 'Sample'
{
// フィールド宣言と FieldInit メタデータの指定
// i と k には HasDefault フラグ(0x8000)で既定値も付与してみました。
// といっても、これは本来、literal 定数フィールドや既定値パラメータ用なので、
// ただのフィールドに指定しても実際には使われないのですが。
.field public int32 'i' = int8(-1)
.field public int32 'j'
.field public int32 'k' = int8(23)
.field public initonly int32 'l'
// Sample クラスのコンストラクタ
.method public hidebysig specialname rtspecialname
instance void '.ctor'() cil managed
{
// 変数宣言
.locals init ( int32 'i', int32 'j', int32 'k' , int32 'l' )
// フィールドに値を書き込む前に、各フィールドの値を変数に退避しておきます
ldarg.0 ldfld int32 'Sample'::'i' stloc.0
ldarg.0 ldfld int32 'Sample'::'j' stloc.1
ldarg.0 ldfld int32 'Sample'::'k' stloc.2
ldarg.0 ldfld int32 'Sample'::'l' stloc.3
// this.i に初期値 -1 を書き込み
ldarg.0 ldc.i4.m1 stfld int32 'Sample'::'i'
// this.j に初期値 7 を書き込み
ldarg.0 ldc.i4.7 stfld int32 'Sample'::'j'
// this.k には書き込まない
nop
// this.l に -123 を書き込み
ldarg.0 ldc.i4.s -123 stfld int32 'Sample'::'l'
// 書き込む前のフィールド値と書き込み後のフィールド値を
// Console.WriteLine で出力します
ldstr "** before: i=({0} => {1})"
ldloc.0 box int32
ldarg.0 ldfld int32 'Sample'::'i' box int32
call void [mscorlib]'System.Console'::'Write'(string, object, object)
ldstr ", j=({0} => {1})"
ldloc.1 box int32
ldarg.0 ldfld int32 'Sample'::'j' box int32
call void [mscorlib]'System.Console'::'Write'(string, object, object)
ldstr ", k=({0} => {1})"
ldloc.2 box int32
ldarg.0 ldfld int32 'Sample'::'k' box int32
call void [mscorlib]'System.Console'::'Write'(string, object, object)
ldstr ", l=({0} => {1})"
ldloc.3 box int32
ldarg.0 ldfld int32 'Sample'::'l' box int32
call void [mscorlib]'System.Console'::'WriteLine'(string, object, object)
ret
}
}
// === グローバル 静的 フィールド ===
.field public static float64 'globalValue'
// === エントリポイント ===
.method static public void 'Main'() cil managed
{
.entrypoint
// 無名変数宣言
// C# コンパイラは、ゼロクリアされるよう init フラグを必ず付与しますが、
// ここではあえて init を外しています。このため、この変数の初期値は不定となり
// 実行されるたびに異なる値になる可能性があります
.locals /* init */ ( uint64, uint32, bool, int64, class Sample )
// 未初期化のグローバル静的フィールド globalValue (double 型)を表示
ldstr "static Double globalField={0};"
ldsfld float64 'globalValue' box float64
call void [mscorlib]'System.Console'::'WriteLine'(string, object)
// 未初期化の 第0変数(ulong型)を表示
ldstr "UInt64 v0={0};"
ldloc.0 box uint64
call void [mscorlib]'System.Console'::'WriteLine'(string, object)
// 未初期化の 第1変数(uint型)を表示
ldstr "UInt32 v1={0};"
ldloc.1 box uint32
call void [mscorlib]'System.Console'::'WriteLine'(string, object)
// 未初期化の 第2変数(bool型)を表示
ldstr "Boolean v2={0}"
ldloc.2 box bool
call void [mscorlib]'System.Console'::'WriteLine'(string, object)
// 未初期化の 第3変数(long型)を表示
ldstr "Int64 v3={0};"
ldloc.s 3 box int64
call void [mscorlib]'System.Console'::'WriteLine'(string, object)
// 第4変数(Sample型)は未初期化だと NullReferenceException になるので
// まずは new Sample() してインスタンス化します
newobj instance void 'Sample'::'.ctor'() stloc.s 4
// 第4変数(Sample)の フィールド i, j を表示
ldstr "** after: i={0}, j={1}"
ldloc.s 4 ldfld int32 'Sample'::'i' box int32
ldloc.s 4 ldfld int32 'Sample'::'j' box int32
call void [mscorlib]'System.Console'::'Write'(string, object, object)
ldstr ", k={0}, l={1}"
ldloc.s 4 ldfld int32 'Sample'::'k' box int32
ldloc.s 4 ldfld int32 'Sample'::'l' box int32
call void [mscorlib]'System.Console'::'WriteLine'(string, object, object)
ldstr "*** done ***"
call void [mscorlib]'System.Console'::'WriteLine'(string)
ret
}
|