循环静态只读字段定义的奇怪行为
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
的字段可以避免该问题。
运行 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
的字段可以避免该问题。