Sunday, August 31, 2008

How to Avoid Duplicated Initialization of Fields


It is said in Jeff's book that initialization work of a field together with its definition will be done in ctors. If you have several ctors, the field will be initialized in these ctors seperately. See following example:
    public partial class Window1 : Window
{
public class InnerObject
{
public InnerObject()
{
Debug.WriteLine("InnerObject() ctor.");
}
}

public class Foo
{
private int field = 99;
private InnerObject innerObject = new InnerObject();

public Foo()
{
}

public Foo(int a)
{
}
}

public Window1()
{
InitializeComponent();

Foo foo1 = new Foo();
Foo foo2 = new Foo(12);
}
}

We call these two ctors. And let's have a look at IL:
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 a) cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 99
IL_0003: stfld int32 FieldInitialization.Window1/Foo::'field'
IL_0008: ldarg.0
IL_0009: newobj instance void FieldInitialization.Window1/InnerObject::.ctor()
IL_000e: stfld class FieldInitialization.Window1/InnerObject FieldInitialization.Window1/Foo::innerObject
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: nop
IL_001c: ret
} // end of method Foo::.ctor

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 99
IL_0003: stfld int32 FieldInitialization.Window1/Foo::'field'
IL_0008: ldarg.0
IL_0009: newobj instance void FieldInitialization.Window1/InnerObject::.ctor()
IL_000e: stfld class FieldInitialization.Window1/InnerObject FieldInitialization.Window1/Foo::innerObject
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: nop
IL_001c: ret
} // end of method Foo::.ctor


Very clear. Two fields, Foo.field and Foo.innerObject have initialization code both in Foo() and Foo(int a). So if you have large number of fields, many ctors, and you care about code size, don't initialize fields with their definition. Put initialization code into one ctor (usually the one don't have parameters, default ctor), and let other cotrs call it. And more, I found another easy way to do this. Just initialize fields with their definition and let other ctors call default ctor, like this:
        public class Foo
{
private int field = 99;
private InnerObject innerObject = new InnerObject();

public Foo()
{
}

public Foo(int a) : this()
{
}
}
And let look at the IL:
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 a) cil managed
{
// Code size 10 (0xa)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void FieldInitialization.Window1/Foo::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: nop
IL_0009: ret
} // end of method Foo::.ctor

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 29 (0x1d)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.s 99
IL_0003: stfld int32 FieldInitialization.Window1/Foo::'field'
IL_0008: ldarg.0
IL_0009: newobj instance void FieldInitialization.Window1/InnerObject::.ctor()
IL_000e: stfld class FieldInitialization.Window1/InnerObject FieldInitialization.Window1/Foo::innerObject
IL_0013: ldarg.0
IL_0014: call instance void [mscorlib]System.Object::.ctor()
IL_0019: nop
IL_001a: nop
IL_001b: nop
IL_001c: ret
} // end of method Foo::.ctor


As you can see initialization code in Foo(int a) is gone. C# compiler did the optimization. I think, it's the better way of initializing fields, though Jeff suggest to do initialization expilicitly in default ctor.

But, I have a question, can we rely on this optimization behavor of C# compiler?

Post a Comment