循环静态只读字段定义的奇怪行为

Strange behaviour with circular static readonly field definitions

运行 Visual Studio 2017(调试版本)中的以下代码时,我遇到了一些奇怪的行为:

using System;
using System.Collections.Generic;

namespace ConsoleApp2
{
    public class Program
    {
        public static class DefaultCustomers
        {
            public static readonly Customer NiceCustomer = new Customer() { Name = "Mr. Nice Guy " };
            public static readonly Customer EvilCustomer = new Customer() { Name = "Mr. Evil Guy " };
            public static readonly Customer BrokeCustomer = new Customer() { Name = "Mr. Broke Guy" };
        }

        public class Customer
        {
            public static readonly IEnumerable<Customer> UnwantedCustomers = new[] { DefaultCustomers.EvilCustomer, DefaultCustomers.BrokeCustomer };

            public string Name { get; set; }

            public override string ToString()
            {
                return Name;
            }
        }

        public static void Main(string[] args)
        {
            Console.WriteLine(new Customer() { Name = "Some other customer" });
            //Console.WriteLine(DefaultCustomers.NiceCustomer);

            foreach (var customer in Customer.UnwantedCustomers)
            {
                Console.WriteLine(customer != null ? customer.ToString() : "null");
            }

            Console.ReadLine();
        }
    }
}

控制台上的输出是

Some other customer
Mr. Evil Guy
Mr. Broke Guy

这大致是我预期的行为。但是,如果我取消注释 Program.Main(...) 中的第二行,输出将更改为

Some other customer
Mr. Nice Guy
null
null

我知道通过将 UnwantedCustomers 变为静态只读 属性 可以轻松解决该问题。

但我想知道所描述的行为是否遵循 类 和对象的初始化顺序,或者此行为是否未定义?

你的初始化顺序有问题。

static 字段(和属性)刚好在静态构造函数 运行 之前初始化(或者 运行 如果有的话)。这是在引用 class 的任何成员之前(静态或非静态)。

注释掉 lime 后,当引用 Customer.UnwantedCustomers 时,它会触发 Customer 的静态构造,该构造之前是 DefaultCustomers.

的静态构造

但是更容易引用 DefaultCustomers 它会触发 DefaultCustomers 的静态构造,这需要 Customer 的静态构造。这意味着 Customer 的静态属性在 DefaultConstomers 的 之前被初始化 。因此为空。在这种情况下,一旦 Customer 的静态构造完成,DefaultCustomers 的静态构造将完成,因此 DefaultCustomers.NiceCustomer 具有值但 Customer.UnwantedCustomers 包含空值。

这是一个定义明确的行为,可以用一种即使没有帮助也可以预测的行为来涵盖此类情况。

你的问题是你的两个类型之间的循环引用。将 UnwantedCustomers 作为 DefaultCustomers 的字段可以避免该问题。