Порядок инициализации статических параметров - PullRequest
2 голосов
/ 21 декабря 2011

Несколько недель назад я перешел с Java на C #.Сегодня у меня было странное поведение, и я пытаюсь воспроизвести его в этом простом примере.Я использую .net FW 4.

У меня есть три класса: первый, абстрактный:

namespace ReadonlyStaticOrder
{
    using System;
    using System.Collections.Generic;

    public abstract class AbstractClass
    {
        public AbstractClass(string value, IEnumerable<string> someValues)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (someValues == null)
            {
                throw new ArgumentNullException("someValues");
            }
            // would do something after...
        }
    }
}

второй:

namespace ReadonlyStaticOrder
{
    using System.Collections.Generic;

    public sealed class ReadonlyOrderInitialization : AbstractClass
    {
        // this line introduces the bug, since it call the ctor before SomeValues already initialized
        // if removed, no more exception
        public static readonly ReadonlyOrderInitialization Sample = new ReadonlyOrderInitialization("sample");

        private static readonly IEnumerable<string> SomeValues = new string[] { "one", "two", "three" };

        public ReadonlyOrderInitialization(string value)
            : base(value, SomeValues)
        {
        }
    }
}

идемонстратор:

namespace ReadonlyStaticOrder
{
    using System;

    public sealed class Program
    {
        static void Main(string[] args)
        {
            try
            {
                new ReadonlyOrderInitialization("test");
            }
            catch (TypeInitializationException typeInitializationException)
            {
                Console.WriteLine(typeInitializationException.Message);
                Console.WriteLine(typeInitializationException.InnerException.Message);
                Console.WriteLine(typeInitializationException.StackTrace);
            }

            Console.ReadLine();
        }
    }
}

И вывод:

Инициализатор типа для ReadonlyStaticOrder.ReadonlyOrderInitialization выдал исключение.Значение не может быть нулевым.Имя параметра: someValues ​​в ReadonlyStaticOrder.1016 *

Я добавил комментарий в строку, которая представляет ошибку.Для меня компилятор должен предупредить меня, что поведение может быть странным из-за порядка статической инициализации.Я не прав?

Спасибо, ребята, и я надеюсь, что у вас достаточно информации.

Ответы [ 2 ]

5 голосов
/ 21 декабря 2011

Это определяется как порядок текста - §17.11 в ECMA 334:

Если класс содержит какие-либо статические поля с инициализаторами, эти инициализаторы выполняются в текстовом порядке непосредственно передк выполнению статического конструктора.

В качестве отступления особенно интересно, если учесть partial classes, , в этом случае: оно не определено .

Если сомневаетесь, переместите инициализацию явно в статический конструктор.


Что касается того, почему;подумайте (примечание: это только мои собственные мысли):

  • «определенное назначение» - это то, что обычно устраняет эту проблему, но «определенное назначение» не применяется к полям
  • анализ кода во всех деталях сложен в вычислительном отношении (возможно, я думаю, что это "остановка") - поэтому он может предложить только неполный вид безопасности (который является искусственным и может привести к проблемам в любомнетривиальный сценарий)
  • и из-за проблемы partial classes полный порядок сам строго не определен;так что не может обрабатывать общий случай - и снова, охват конкретного случая (фрагмент одного класса и т. д.) возвращается к «тонкой фанере» (где он предупреждает только об очевидных случаях, но не могу помочь с нетривиальными)
1 голос
/ 21 декабря 2011

Обычно компилятор предупреждает вас, если вы пытаетесь использовать элементы до их инициализации.

В этом случае вы обойдете эту проверку, так как статический член не использует другой статический член напрямую, вместо этого он вызывает конструктор, который использует другой статический член.

Компилятор не может защитить вас от всех возможных проблем с зависимостями, только от простых. Это всего лишь один шаг, слишком сложный для компилятора.

Конечно, было бы возможно для компилятора поймать что-то подобное, но это усложнило бы его для каждого дополнительного уровня зависимости, и все еще невозможно отловить каждый ситуация.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...