在Visual Studio 2017(Debug build)中运行以下代码时,我有一些奇怪的行为:
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转换为静态readonly属性来轻松解决此问题。
但是我不知道所描述的行为是否遵循类和对象的初始化顺序,或者该行为是否未定义?
答案 0 :(得分:6)
您有一个初始化问题。
static
字段(和属性)在静态构造函数运行之前初始化(如果有,则将运行)。这是在引用对类的任何成员的引用之前(无论是否静态)。
注释掉石灰后,在引用Customer.UnwantedCustomers
时会触发Customer
的静态构造,然后触发DefaultCustomers
的静态构造。
但是通过更容易地引用DefaultCustomers
,它会触发DefaultCustomers
的静态构造,这需要Customer
的静态构造。这意味着Customer
的静态属性在DefaultConstomers
的静态属性之前被初始化。并且因此为null。在这种情况下,Customer
的静态构造完成后,DefaultCustomers
的静态构造将完成,因此DefaultCustomers.NiceCustomer
具有一个值,但是Customer.UnwantedCustomers
包含空值。
这是定义明确的行为,以无帮助的可预测行为来涵盖此类情况。
您的问题是两种类型之间的循环引用。将UnwantedCustomers
设置为DefaultCustomers
的字段可以避免此问题。