C# 什么';这两种实例化类属性的方法有什么区别?

C# 什么';这两种实例化类属性的方法有什么区别?,c#,C#,这里是基本的C#问题 在声明类属性/字段时或在相关对象的构造函数中创建该类属性/字段的实例之间有什么区别。例如: public class MyClass { public MyObject = new MyObject(); } vs 带有初始值设定项的字段在调用基构造函数之前初始化,而如果初始值设定项在主体中,则只有在调用基构造函数之后才会执行 如果基构造函数调用虚拟方法,这可能是相关的,但就我个人而言,我会尽量避免这种情况 示例代码: public class Base {

这里是基本的C#问题

在声明类属性/字段时或在相关对象的构造函数中创建该类属性/字段的实例之间有什么区别。例如:

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


带有初始值设定项的字段在调用基构造函数之前初始化,而如果初始值设定项在主体中,则只有在调用基构造函数之后才会执行

如果基构造函数调用虚拟方法,这可能是相关的,但就我个人而言,我会尽量避免这种情况

示例代码:

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
    }
}

需要注意的是,(在C#)对字段的初始值设定项赋值将在调用任何基类构造函数之前发生(关于VB是否可以被迫做同样的事情的说明就是明证)

这意味着您不能使用初始值设定项语法引用基类的字段(即,您不能直接将此VB转换为C#):


您还可以使用静态构造函数,该构造函数在可以初始化静态变量的任何其他构造函数之前被调用

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();
    }
}
我编译这些C代码:

我得到了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 test.MyObject test.MyClass2::MyObject)的调用顺序

在第一种情况下,MyClass1初始化如下:

  • MyObject初始值设定项
  • 类初始值设定项(构造函数分配)
  • 基类初始值设定项(基类构造函数)
但是,MyClass2按以下顺序初始化:

  • 基类初始值设定项(基类构造函数)
  • MyObject初始值设定项
  • 类初始值设定项(构造函数分配)

哈哈,我正准备把这篇文章作为对乔恩·斯基特答案的评论。@Artur:你不先看一下,怎么会投下否决票呢?您的小测试用C#编译了代码。Damien在这里的回答以及他链接到的明确指出,VB.NET中的行为不同。@Artur-哪一点不正确?答案(Class2)中的赋值
MyObject=new MyObject()
不是初始值设定项,它只是一个赋值语句。初始值设定项在C#@Cody Gray中有一个特定的含义:“…在C#中,对字段的初始值设定项赋值将在调用任何基类构造函数之前发生”-这是一个LIE@Damien_The_Unbeliever在MyClass2中,在基类构造函数调用+1之后也会调用该字段的初始值设定项assignment,我的理解是,在声明处初始化字段允许编译器/JITer进行一些优化,否则它不会在构造函数中进行初始化。我相信我在“.NET Framework设计指南”中读到了这一点,作为他们将字段初始化作为“最佳实践”的理由,但我不再确定了。@Cody:说实话,我不确定。我个人在编写代码时不会考虑这一点——我只会坚持用最清晰的方式表达意图。@Jon Skeet错误:子类没有继承到基中code@JonSkeet,关于问题,编译器将示例2(有问题)转换为示例1,如果它是在Reflector@Adeel:依我看,这基本上是Reflector中的一个bug。看看IL(您仍然可以在Reflector中执行),您会发现在赋值顺序和基本构造函数调用方面存在差异。
public class MyClass1
{
    public MyObject MyObject = new MyObject();
}
public class MyClass2
{
    public MyObject MyObject;

    public MyClass2()
    {
        MyObject = new MyObject();
    }
}
.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
.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