Подтвердить что ты не робот

С# - Какая разница между этими двумя способами создания свойства класса?

Основной вопрос С# здесь.

В чем разница между созданием экземпляра свойства/поля класса либо при его объявлении, либо в конструкторе рассматриваемого объекта. Например:

public class MyClass
{
    public MyObject = new MyObject();
}

против

public class MyClass
{
    public MyObject;

    public MyCLass()
    {
        MyObject = new MyObject();
    }
}
4b9b3361

Ответ 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