我的 C# 程序不会按照我认为的方式初始化对象,它必须根据对象初始化顺序.为什么?



我相信 C# 的对象初始化顺序是这样的:

  • 派生静态字段
  • 派生静态构造函数
  • 派生实例字段
  • 基本静态字段
  • 基静态构造函数
  • 基本实例字段
  • 基本实例构造函数
  • 派生实例构造函数

下面你会看到一个简单的测试程序,以及当我运行它时它产生的输出。

public class UiBase
{
protected static string Something = "Hello";
public UiBase()
{
Console.WriteLine(this.ToString());
}
}
public class Point : UiBase
{
private int X = -1;
private int Y = -1;
static Point()
{
Something = "Bar";
}
public Point(int x, int y)
{
X = x;
Y = y;
}
public override string ToString()
{
return $"Point:{X}/{Y}/{Something}";
}
}
public static class Program{
public static void Main(){
var x = new Point(2,1);
Console.WriteLine(x);
}
on Console:
Point:-1/-1/Bar
Point:2/1/Bar

当我根据上面的列表考虑它应该如何发生时,我认为它应该是这样的:

  1. 点静态字段(在我的情况下没有?
  2. 点静态构造函数 -> 将某些内容设置为"Bar">
  3. 点实例字段
  4. 基本静态字段 -> 将某些内容设置回"Hello"? ...

但是,它并没有将某些东西设置回 Hello 这真的让我感到困惑。那么我该如何解释呢?还是对象初始化与我所说的不同?

在基UiBase类构造函数中调用虚拟成员ToString()

Console.WriteLine(this.ToString());

它在构造函数之前被调用Point

public Point(int x, int y)
{
X = x;
Y = y;
}

this尚未完全初始化,则输出-1。由于ToString()是虚拟方法,因此根据规范调用Point.ToString()

调用最派生类中的重写成员,该成员可能 如果没有派生类重写该成员,则为原始成员。

在创建Point实例或引用任何静态成员之前自动调用静态构造函数(有关详细信息,请查看静态构造函数(

static Point()
{
Something = "Bar";
}

它将覆盖基类中的Something,并且在这两种情况下,您都会获得Bar输出。Something永远不会被设置回Hello,它只被覆盖一次。

Something字段完全特定于UiBasePoint类中没有副本,它的值将在任何地方更改。根据静态成员

静态成员只有一个副本,无论有多少个 将创建类的实例。

如果你在Console.WriteLine(x);后打印UiBase.Something,你会得到Bar,而不是Hello。对于泛型类,只有一个例外,但它超出了您的问题范围。

就执行顺序而言,所有字段初始值设定项都按从派生类到基的顺序运行,然后所有构造函数按从基到派生的顺序运行(这对于实例成员是正确的(。我为您的所有操作添加了一个步骤,以查看实际订单。

public class UiBase
{
private static int temp = Step("uibase static field init");
public static string Something = "Hello";
private int _temp = Step("uibase instance field init");
public static int Step(string message)
{
Console.WriteLine(message);
return 0;
}
public UiBase()
{
Step("uibase instance ctor");
Console.WriteLine(this.ToString());
}
}
public class Point : UiBase
{
private int _temp = Step("point instance field init");
private int X = -1;
private int Y = -1;
static Point()
{
Step("point static ctor before");
Something = "Bar";
Step("point static ctor after");
}
public Point(int x, int y)
{
Step("point instance ctor");
X = x;
Y = y;
}
public override string ToString()
{
return $"Point:{X}/{Y}/{Something}";
}
}

输出将如下所示

point static ctor before
uibase static field init
point static ctor after
point instance field init
uibase instance field init
uibase instance ctor
Point:-1/-1/Bar
point instance ctor
Point:2/1/Bar

首先调用Point静态构造函数(Point类中没有静态字段(,然后它会"询问"UiBase初始化静态字段,因为访问其Something值(设置为Hello(,之后Something设置为Bar并且执行继续实例初始化(同样,Something不再更改( - 派生类字段, 基类字段、基类构造函数和派生类构造函数。

我认为,只有前 3 行可能会有点混乱,但静态初始化只发生一次,并且在任何实例初始化之前。静态初始化的顺序由编译器根据您的实际代码确定。

添加UiBase静态构造函数可以使图片实际上更清晰,在这种情况下UiBase静态成员将在Point静态初始化之前初始化。

最新更新