Существует ли стандартный способ для .Как будут работать загрузчики сетевых классов?

#c# #.net #vb.net #classloader #static-constructor

#c# #.net #vb.net #classloader #статический конструктор

Вопрос:

Существует ли стандартный способ для .Как будут работать загрузчики сетевых классов?

Допустим, я скомпилирую этот код:

 Option Strict On : Option Explicit On

Module Module1
    Sub Main()
        System.Diagnostics.Debug.WriteLine("Main")
        Dim g = C.A
    End Sub
End Module

Public Class C
    Shared Sub New()
        System.Diagnostics.Debug.WriteLine("Init C")
    End Sub
    Shared Property A As New A
End Class

Public Class A
    Shared Sub New()
        System.Diagnostics.Debug.WriteLine("Init A")
    End Sub
    Public Sub New()
        System.Diagnostics.Debug.WriteLine("A Constructor")
    End Sub
End Class
  

Могу ли я гарантировать, что скомпилированный код будет (на всех реализованных платформах) иметь следующий результат?

 Main
Init A
A Constructor
Init C
  

Комментарии:

1. «Загрузчик классов» означает что-то другое. Вы говорите об «инициализаторе класса», он же «статический конструктор».

2. Джон Скит обсуждает инициализацию типа (с некоторыми изменениями в .NET 4) здесь: msmvps.com/blogs/jon_skeet/archive/2010/01/26 /…

3. @Hans: Возможно, но также разумно задаться вопросом, гарантированно ли загрузчик классов среды выполнения вызовет инициализатор типа в определенное время. (Фактически, загрузчику классов разрешено делать это только для отмеченных типов beforefieldinit , в противном случае инициализатор типа должен быть вызван позже, как указано в спецификации)

Ответ №1:

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

Комментарии:

1. В то же время «тот же IL» оставляет среде выполнения некоторую свободу действий в отношении того, когда именно запускаются инициализаторы типа, особенно когда используется beforefieldinit .

Ответ №2:

Поскольку вы используете конструкторы, а не встроенную инициализацию, компилятор VB не помечает типы beforefieldinit , и порядок точно контролируется.

Если, однако, вы написали следующее (используя встроенную инициализацию):

 Option Strict On : Option Explicit On

Module Module1
    Sub Main()
        System.Console.WriteLine("Main")
        Dim g = C.A
    End Sub
End Module
Public Class C
    Shared Function Narg() As A
        Dim alpha As New A
        System.Console.WriteLine("Init C")
        Return alpha
    End Function
    Shared Property A As A = Narg()
End Class
Public Class A
    Shared Sub New()
        System.Console.WriteLine("Init A")
    End Sub
    Public Sub New()
        System.Console.WriteLine("A Constructor")
    End Sub
End Class
  

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

 Init A
A Constructor
Init C
Main
  

Причина в том, что C now помечен beforefieldinit .


Беспричинное придирчивое отношение: Нет, ваш исходный код не гарантированно будет иметь такой результат. При сборке релиза он не будет иметь выходных данных, потому что System.Diagnostics.Debug вызывается условно.

Комментарии:

1. Я не понимаю, как этот вывод является законным: экземпляр A создается через New непосредственно перед созданием Init C вывода. Оптимизатор переупорядочивает инструкции? Почему здесь это разрешено? Это не имеет никакого отношения к порядку инициализации (!), у нас просто есть два смежных оператора с побочными эффектами.

2. @Konrad: beforefieldinit позволяет C инициализатору запускаться в любое время перед C.A использованием. Итак, в этом случае он фактически выполняется раньше Main . Init A - A Constructor - Init C Порядок не изменился (и не может измениться). В Narg() создается новый A, который вызывает A Constructor . Но A Constructor не может запуститься, пока не будет запущен инициализатор для A , поэтому JIT запускает его первым. После A Constructor возврата Narg() продолжает запуск и печатает Init C . Это приводит к упомянутой подпоследовательности.

3.Пока все хорошо. Но тогда конструктор A также должен быть запущен раньше Main , потому что он вызывается внутри инициализатора перед выводом на консоль "Init C" .

4. @Konrad: Конструктор A действительно запускался раньше Main , хотя это не гарантировано. Гарантируется только то, что он выполняется до того, как строка Init C будет напечатана, что разрешено beforefieldinit либо сразу после Main печати, либо в любое время раньше.

5. А, понятно. Я просто как-то неправильно прочитал ваш вывод и подумал, что Init C стоял * раньше A Constructor . Моя ошибка.