С# - Какая разница между этими двумя способами создания свойства класса?
Основной вопрос С# здесь.
В чем разница между созданием экземпляра свойства/поля класса либо при его объявлении, либо в конструкторе рассматриваемого объекта. Например:
public class MyClass
{
public MyObject = new MyObject();
}
против
public class MyClass
{
public MyObject;
public MyCLass()
{
MyObject = new MyObject();
}
}
Ответы
Ответ 1
Поле с инициализатором инициализируется до вызова базового конструктора, тогда как если инициализатор находится в теле, он запускается только после вызова базового конструктора.
Это может иметь значение, если базовый конструктор вызывает виртуальный метод - но лично я попытаюсь избежать этой ситуации.
Пример кода:
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
}
}
Ответ 2
Я скомпилирую код С#:
public class MyClass1
{
public MyObject MyObject = new MyObject();
}
public class MyClass2
{
public MyObject MyObject;
public MyClass2()
{
MyObject = new MyObject();
}
}
Я получил сборку IL:
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
Совершенно ясно, что разница заключается только в порядке вызова конструктора базового класса (System.Object::. ctor()), инициализатора MyObject (test.MyObject::. ctor()) и инициализатора класса (тест stfld class.MyObject test.MyClass2:: MyObject)
В первом случае MyClass1 инициализируется следующим образом:
- Инициализатор MyObject
- инициализатор класса (присваивание конструктора)
- инициализатор базового класса (конструктор базового класса)
Но MyClass2 инициализирует этот порядок:
- инициализатор базового класса (конструктор базового класса)
- Инициализатор MyObject
- инициализатор класса (присваивание конструктора)
Ответ 3
Вы также можете использовать статический конструктор, который вызывается перед любым другим конструктором, где вы можете инициализировать статические переменные
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();
}
}
Ответ 4
Важно отметить, что (в С#) назначение инициализатора в поле будет происходить до вызова любого конструктора базового класса (как показано в этом вопросе о том, VB можно заставить сделать то же самое).
Это означает, что вы не можете использовать синтаксис инициализации для ссылки на поле вашего базового класса (т.е. вы не можете напрямую перевести этот VB в С#):
Public Class Base
Protected I As Int32 = 4
End Class
Public Class Class2
Inherits Base
Public J As Int32 = I * 10
End Class