Ответ 1
Это ответ:
II.10.5.3.1 - II.10.5.3.3 ECMA 335 достаточно четко объясняет это и объясняет большую часть приведенного ниже поведения.
Это не ответ, но далеко не большой для комментария.
Это кажется мне показательным:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(D.Field);
Console.WriteLine(C.Field);
Console.WriteLine(C.FieldX);
Console.WriteLine(D.FieldY);
Console.ReadLine();
}
}
}
class D
{
public static int Field = C.FieldX;
public static int FieldY = C.Field;
}
class C
{
public static int FieldX = 5;
public static int Field = D.Field;
}
Что надежно выводит: 5 0 5 0
С аналогичными результатами для ссылочного типа.
Я верю твоему. Итак, похоже, что на практике происходит то, что триггерная точка в C..cctor для построения D игнорируется, поскольку конструкция D уже началась ". false. Фактическое намерение" Если отмечено значение BeforeFieldInit, то метод инициализации типов запускается или когда-то раньше имеет доступ к любому статическому полю, определенному для этого типа". кажется мне, и действительно в pracice (в этом примере):
CLR распознает основные виды использования D, хочет инициализировать D, распознает D использует C, хочет инициализировать C и до начала D-инициализации действительно начинает. (см. II.10.5.3.3)
то есть C строго инициализируется до D, а любые ссылки на D будут иметь значение null/default.
В моем примере замените первые две основные строки
static void Main(string[] args)
{
Console.WriteLine(C.Field);
Console.WriteLine(D.Field);
И D сначала будет строго инициализирован, вывод
0 0 5 0
Теперь мой вопрос в том, каков ваш настоящий вопрос! т.е. у вас есть пример круговой зависимости, близкий к тому, что вам нужно ".
Также обратите внимание, что поведение здесь полностью аналогично
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(D.Field);
Console.WriteLine(D.FieldY);
Console.ReadLine();
}
}
}
class D
{
public static int Field = FieldY;
public static int FieldY = 5;
}
Что такое 0 5
Я чувствую, что "тот же" пример, но в рамках одной инициализации типов, т.е. вы можете полагаться на "существование" полей, но не на инициализацию, и если вы хотите больше детерминированного упорядочения, тогда пишите статические конструкторы (которые IIRC останавливает перед полем) и создает более детерминированный подход в II.10.5.3.3.