C# - What's the difference between these two ways of instancing a class property?
Basic C# question here.
What is the difference between creating an instance of a class property / field either as you declare it or in the constructor of the object in question. For example:
public class MyClass
{
public MyObject = new MyObject();
}
vs
public class MyClass
{
public MyObject;
public MyCL开发者_开发问答ass()
{
MyObject = new MyObject();
}
}
A field with an initializer is initialized before the base constructor is called, whereas if the initializer is in the body, that only gets executed after the base constructor is called.
This can be relevant if the base constructor calls a virtual method - but personally I'd try to avoid that situation.
Sample code:
public class Base
{
public Base()
{
Dump();
}
public virtual void Dump() {}
}
public class Child : Base
{
private string x = "Initialized at declaration";
private string y;
public Child()
{
y = "Initialized in constructor";
}
public override void Dump()
{
Console.WriteLine(x); // Prints "Initialized at declaration"
Console.WriteLine(y); // Prints "" as y is still null
}
}
I compile these C# code:
public class MyClass1
{
public MyObject MyObject = new MyObject();
}
public class MyClass2
{
public MyObject MyObject;
public MyClass2()
{
MyObject = new MyObject();
}
}
I got IL assembly:
MyClass1:
.class public auto ansi beforefieldinit test.MyClass1
extends [mscorlib]System.Object
{
.field public class test.MyObject MyObject
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// code size: 19 (0x13)
.maxstack 8
IL_0000: ldarg.0
IL_0001: newobj instance void test.MyObject::.ctor()
IL_0006: stfld class test.MyObject test.MyClass1::MyObject
IL_000b: ldarg.0
IL_000c: call instance void [mscorlib]System.Object::.ctor()
IL_0011: nop
IL_0012: ret
} // end of method MyClass1::.ctor
} // end of class test.MyClass1
MyClass2:
.class public auto ansi beforefieldinit test.MyClass2
extends [mscorlib]System.Object
{
.field public class test.MyObject MyObject
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// code size: 21 (0x15)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: newobj instance void test.MyObject::.ctor()
IL_000e: stfld class test.MyObject test.MyClass2::MyObject
IL_0013: nop
IL_0014: ret
} // end of method MyClass2::.ctor
} // end of class test.MyClass2
It is prefectly clear that difference is only in the order of call to the base class constructor (System.Object::.ctor()), MyObject initializer (test.MyObject::.ctor()) and the class initializer (stfld class test.MyObject test.MyClass2::MyObject)
In the first case, MyClass1 initializes as follows:
- MyObject initializer
- class initializer (constructor assigment)
- base class initializer (base class constructor)
But, MyClass2 initializes by that order:
- base class initializer (base class constructor)
- MyObject initializer
- class initializer (constructor assigment)
You can also use a static constructor that gets called before any other constructor where you can init static variables
public class Bus
{
private static object m_object= null;
// Static constructor:
static Bus()
{
m_object = new object();
System.Console.WriteLine("The static constructor invoked.");
}
public static void Drive()
{
System.Console.WriteLine("The Drive method invoked.");
}
}
class TestBus
{
static void Main()
{
Bus.Drive();
}
}
It's important to note that (in C#) the initializer assignment to the field will occur before the call to any base class constructor (as evidenced in this question about whether VB could be forced to do the same thing).
This means you can't use initializer syntax to reference a field of your base class (i.e. you can't directly translate this VB into C#):
Public Class Base
Protected I As Int32 = 4
End Class
Public Class Class2
Inherits Base
Public J As Int32 = I * 10
End Class
精彩评论