简体   繁体   English

我的 C# 程序没有按照我认为的对象初始化顺序初始化对象。 为什么?

[英]My C# program does not initialize the Object how I think it has to according to the Object initialization order. Why?

I believe the Object Initialization order of C# goes like this:我相信 C# 的对象初始化顺序是这样的:

  • Derived static fields派生静态字段
  • Derived static constructor派生静态构造函数
  • Derived instance fields派生实例字段
  • Base static fields基本静态字段
  • Base static constructor基本静态构造函数
  • Base instance fields基本实例字段
  • Base instance constructor基础实例构造函数
  • Derived instance constructor派生实例构造函数

Below you see a simple test program and the output it produces when I run it.下面你会看到一个简单的测试程序和它在我运行时产生的输出。

    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

When I think about how it should happen according to the list above, I believe it should be like this:当我根据上面的列表思考它应该如何发生时,我认为它应该是这样的:

  1. Point static field (none in my case?)点静态字段(在我的情况下没有?)
  2. Point static constructor -> sets Something to "Bar"点静态构造函数 -> 将Something设置为“Bar”
  3. Point instance fields点实例字段
  4. Base static fields -> sets Something back to "Hello"?基本静态字段-> 将Something 设置回“Hello”? ... ...

However it does NOT set Something back to Hello which really confuses me.然而,它并没有将Something设置回Hello,这真的让我感到困惑。 So how can I explain this?那么我该如何解释呢? or is the Object Initialization different from what I stated?还是对象初始化与我所说的不同?

You call a virtual member ToString() in base UiBase class constructor您在基UiBase类构造函数中调用虚拟成员ToString()

Console.WriteLine(this.ToString());

It's being called before Point constructor它在Point构造函数之前被调用

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

this isn't fully initialized yet, you are getting -1 in output. this还没有完全初始化,您在输出中得到-1 Since ToString() is virtual method, Point.ToString() is called, per specs由于ToString()是虚方法, Point.ToString()根据规范调用Point.ToString()

The overriding member in the most derived class is called, which might be the original member, if no derived class has overridden the member.调用最派生类中的覆盖成员,如果没有派生类覆盖该成员,则该成员可能是原始成员。

Static constructor is called automatically before the instance of Point is created or any static members are referenced (have a look at the static constructors for details)在创建Point实例或引用任何静态成员之前自动调用静态构造函数(查看静态构造函数了解详细信息)

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

It'll overwrite Something from base class and you get Bar in output in both cases.它会覆盖来自基类的Something ,并且在这两种情况下你都会在输出中得到Bar Something is never set back to Hello , it's being overwritten only once. Something永远不会被设置回Hello ,它只会被覆盖一次。

Something field is entirely specific for UiBase , there is no copy in Point class, it's value will be changed everywhere. Something字段完全特定于UiBasePoint类中没有副本,它的值将随处更改。 According to static members根据静态成员

Only one copy of a static member exists, regardless of how many instances of the class are created.无论创建了多少个类的实例,都只存在一个静态成员的副本。

If you print UiBase.Something after Console.WriteLine(x);如果你在Console.WriteLine(x);之后打印UiBase.Something Console.WriteLine(x); , you'll get Bar , not the Hello . ,你会得到Bar ,而不是Hello There is only one exception, for generic classes, but it's out of scope of your question.对于泛型类,只有一个例外,但这超出了您的问题范围。

In terms of execution order all field initializers run in order from derived class to base, and then all constructors run in order from base to derived (this is correct for instance members).在执行顺序方面,所有字段初始值设定项按从派生类到基类的顺序运行,然后所有构造函数按从基类到派生的顺序运行(这对于实例成员是正确的)。 I've added a steps for all your operations to see the actual order.我为您的所有操作添加了一个步骤以查看实际订单。

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

the output will be the following输出将如下

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

The Point static constructor is invoked first (there is no static fields in Point class), then it'll 'ask' UiBase init a static fields, because accessing its Something value (it's set to Hello ), after that Something is set to Bar and execution continues to instance initialization (again, Something never changes anymore) - derived class fields, base class fields, base class constructor and derived class constructor. Point静态构造函数首先被调用( Point类中没有静态字段),然后它会“询问” UiBase初始化一个静态字段,因为访问它的Something值(它被设置为Hello ),之后Something被设置为Bar并继续执行实例初始化(同样, Something永远不会再改变)-派生类字段、基类字段、基类构造函数和派生类构造函数。

I think, only first 3 lines can be confusing a little bit, but static initialization happens only once and before any instance initialization.我认为,只有前 3 行可能会有点混乱,但静态初始化只发生一次,并且在任何实例初始化之前。 The order of static initialization is determined by compiler based on your actual code.静态初始化的顺序由编译器根据您的实际代码确定。

Adding a UiBase static constructor can make the picture more clear actually, in this case UiBase static members will be initialized before Point static initialization.添加一个UiBase静态构造函数其实可以让画面更加清晰,在这种情况下UiBase静态成员会在Point静态初始化之前被初始化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM