Можно ли принудительно инициализировать переменные экземпляра VB.NET ДО вызова конструктора базового типа? - PullRequest
27 голосов
/ 05 января 2011

После отладки особенно сложной проблемы в VB.NET, связанной с порядком инициализации переменных экземпляра, я обнаружил, что существует существенное несоответствие между поведением, которое я ожидал от C #, и фактическим поведением в VB.NET.

Примечание: Этот вопрос касается небольшого расхождения в поведении VB.NET и C #. Если вы фанат языка, который не может дать ответ, отличный от ", поэтому вам следует использовать C #, noob" , вам нечего здесь видеть; любезно двигаться вперед.

В частности, я ожидал поведения, описанного в C # Language Specification (выделение добавлено):

Когда конструктор экземпляра не имеет инициализатора конструктора или у него есть инициализатор конструктора в форме base(...), этот конструктор неявно выполняет инициализации, указанные переменными-инициализаторами полей экземпляра, объявленных в его классе. Это соответствует последовательности присваиваний, которые выполняются сразу после входа в конструктор и до неявного вызова конструктора прямого базового класса. Инициализаторы переменных выполняются в текстовом порядке, в котором они появляются в классе декларация.

Сравните это с частью Спецификации языка VB.NET, касающейся Конструкторов экземпляров , которая гласит (выделение добавлено):

Когда первый оператор конструктора имеет форму MyBase.New(...), конструктор неявно выполняет инициализации, заданные инициализаторами переменных переменных экземпляра, объявленных в типе. Это соответствует последовательности назначений, которые выполняются сразу после вызова прямого конструктора базового типа. Такое упорядочение гарантирует, что все переменные базового экземпляра инициализируются инициализаторами своих переменных до выполнения любых операторов, имеющих доступ к экземпляру. .

Несоответствие здесь сразу очевидно. C # инициализирует переменные уровня класса до вызова базового конструктора. VB.NET делает в точности наоборот, по-видимому, предпочитая вызывать базовый конструктор перед установкой значений полей экземпляра.

Если вы хотите увидеть какой-то код, этот связанный вопрос предоставляет более конкретный пример расходящегося поведения. К сожалению, он не дает никаких подсказок относительно того, как можно заставить VB.NET следовать модели, установленной C #.

Меня меньше интересует, почему разработчики двух языков выбрали такие расходящиеся подходы, чем я в возможных обходных путях решения проблемы. В конечном счете, мой вопрос заключается в следующем: Есть ли способ, которым я могу написать или структурировать свой код в VB.NET, чтобы принудительно инициализировать переменные экземпляра до , конструктор базового типа называется, как стандартное поведение в C #?

Ответы [ 2 ]

8 голосов
/ 05 января 2011

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

Это немного грязно и может представлять незначительное снижение производительности, но в VB вы мало что можете сделать.

2 голосов
/ 11 февраля 2011

Любой хорошо написанный класс должен либо гарантировать, что любые виртуальные члены, которые могут быть вызваны на частично сконструированном экземпляре, будут вести себя разумно.C # придерживается философии, что экземпляр класса, чьи инициализаторы полей были запущены, будет в достаточно разумном состоянии, чтобы позволить использовать виртуальные методы.VB.net придерживается философии, заключающейся в том, что разрешение инициализаторам поля использовать частично сконструированный объект (и - с небольшой работой - любые параметры, передаваемые в конструктор) - более полезно, чем гарантия того, что инициализаторы поля будут работать до любого виртуальногометоды называются.

ИМХО, правильный подход с точки зрения языкового дизайна состоял бы в том, чтобы предоставить удобные средства указания того, что указанные инициализаторы полей должны быть запущены "рано" или "поздно", так как есть временакаждый из них может быть полезен (хотя я предпочитаю «поздний» стиль, поскольку он позволяет сделать параметры конструктора доступными для инициализаторов полей).Например:

Class ParamPasserBase(Of T) ' Generic class for passing one constructor parameter
  Protected ConstructorParam1 As T
  Sub New(Param As T)
    ConstructorParam1 = Param
  End SUb
End Class
Class MyThing
  Inherits ParamPasserBase(Of Integer)
  Dim MyArray(ConstructorParam1-1) As String
  Sub New(ArraySize As Integer)
    MyBase.New(ArraySize)
  End Sub
  ...
End Class

В C # нет хорошего способа, чтобы объявления полей или инициализаторы использовали аргументы, передаваемые конструктору.В VB это можно сделать достаточно чисто, как показано выше.Обратите внимание, что в vb также можно использовать параметр конструктора по ссылкам, чтобы вывезти копию строящегося объекта до запуска инициализаторов полей;если подпрограмма Dispose класса правильно написана для работы с частично созданными объектами, объект, который добавляет его конструктор, может быть должным образом очищен.

...