#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
. Моя ошибка.