2015-07-27 2 views
0

Кажется, что проблема связана с инициализацией вложенных статических классов перед их родителем. См. Пример ниже, я включил комментарии, чтобы указать порядок, в котором я ожидал, что вещи будут инициализированы/вызваны, и фактический порядок. В этом случае я звоню Test1.Test2.GetName(), поэтому я ожидал, что статические классы инициализируются в следующем порядке: Test1, Test2Вложенные статические классы, инициализированные в неправильном порядке?

using System; 
using System.Collections.Generic; 

namespace ConsoleApplication1 { 
    class Program { 
     static void Main(string[] args) { 
      Console.WriteLine(Test1.Test2.GetName()); 
      Console.ReadKey(); 
     } 
    } 

    public static class Test1 { 
     // Actual Order: 2; Expected: 1 
     private static string Name = "Test1"; 
     public static List<string> Names { get; private set; } 

     // Actual Order: 3; Expected: 2 
     static Test1() { 
      Test1.Names = new List<string>(new string[] { 
       Test2.GetName() 
      }); 
     } 

     public static class Test2 { 
      // Actual Order: 1; Expected: 3 
      private static string Name = Test1.Name.ToString() + "_Test2"; 

      // Actual Order: 4; Expected: 4 
      public static string GetName() { 
       return Name.ToString(); 
      } 
     } 
    } 
} 

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

Test2.Name ->Test1.Name ->Test1() ->Test2.GetName() ->Test2.Name

Таким образом, хотя Test2.Name является точкой входа, в теории, не инициализируются времени это необходимо.

+1

Хорошо освещенный в [литература] (https://msdn.microsoft.com/en-us/library/k9x6w0hc.aspx). Цитата: «Пользователь не имеет контроля над тем, когда статический конструктор выполняется в программе». Другими словами, порядок не является «неправильным», он не определен. –

+0

Он также говорит: «вызывается автоматически до создания первого экземпляра или ссылки на какие-либо статические члены», что в этом случае неверно, так как сначала инициализирован вложенный статический класс! Я не ожидаю, что у меня будет контроль, но я должен принять порядок, в котором это произойдет. –

+0

Вместо этого вы должны полагаться на методы инициализации. Таким образом вы контролируете инициализацию. В худшем случае вы можете инициализировать их из статического конструктора в программе. – Bauss

ответ

4

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

Проблема в том, что вложенный класс нестатический член своего родителя. Это совсем другой класс. У этого есть nestedattribute, но это только для видимости членов.

От ECMA-335 (консоль спецификация):

I.8.11.5 определение вложенного типа
Вложенное определение типа идентично определение типа верхнего уровня, за одним исключением: верхнее -level имеет атрибут видимости, тогда как видимость вложенного типа такая же, как видимость охватывающего типа.

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

Если вложенный класс требует его родительского класса для инициализации первого, вы можете явно заставить его в статическом конструкторе:

static Test2() 
{ 
    RuntimeHelpers.RunClassConstructor(typeof(Test1).TypeHandle); 
} 

Здесь смысл кристально чистые.

Но это не сделает ваш пример действительно, так как он имеет циклическую зависимость:

  • Test1 статический конструктор вызывает Test2.GetName(), что означает, что он вызовет инициализацию Test2
  • Test2 статического конструктора (неявный) вызывает Test1.Name.ToString() + "_Test2", что вызовет инициализацию Test1

в любом случае, это не будет работать, вы должны исправить этот код.

+0

Понял. Хотя я все же предполагаю, что вложенные классы должны инициализироваться после их родителя, хотя отношения в основном бессмысленны. Если бы был выбор порядка, он должен быть внешним для внутреннего. В моем случае это всего лишь пара вложенных статических классов, поддерживающих настройки, поэтому я могу сделать что-то вроде 'Settings.UI.Something.SomeValue' - здесь не будет никакой серьезной работы, поэтому это спорный вопрос. Я просто установил родительский класс для инициализации дочерних элементов в его статическом конструкторе и назвал его днем. Благодаря! –

Смежные вопросы