Замещение
Термин «замещение» (shadowing) встречался и в ранних версиях VB, и в большинстве языков программирования. Локальная переменная, имя которой совпадает с именем переменной, обладающей более широкой областью видимости, замещает (скрывает) эту переменную. Кстати, это одна из причин, по которой переменным уровня модуля обычно присваиваются префиксы m_, а глобальные переменные снабжаются префиксами g_ — грамотный выбор имен помогает избежать ошибок замещения. Переопределение унаследованного метода тоже можно рассматривать как своего рода замещение. В VB .NET поддерживается еще одна, чрезвычайно мощная разновидность замещения:
Член производного класса, помеченный ключевым словом Shadows (которое впервые появилось в бета-версии 2), замещает все одноименные члены базового класса.
При помощи ключевого слова Shadows можно определить в производном классе функцию, имя которой совпадает с именем процедуры базового класса. С практической точки зрения ключевое слово Shadows приводит к тому, что в производном классе появляется абсолютно новый член с заданным именем, в результате чего все одноименные унаследованные члены становятся недоступными в производном классе. Из этого следует, что унаследованные члены класса с ключевым словом Shadows невозможно переопределить, поэтому полиморфизм перестает работать.
По умолчанию VB .NET разрешает замещение членов классов, но при отсутствии клю-чевого слова Shadows выдается предупреждение. Кроме того, если один член класса объявляется с ключевым словом Shadows или Overloads, это ключевое слово должно использоваться и для остальных членов класса с тем же именем.
Иногда замещение усложняет ситуацию и приводит к возникновению нетривиальных ошибок — например, при полиморфном вызове замещенных методов и свойств через объект базового класса. Чтобы рассмотреть эти проблемы на конкретном примере, мы внесем некоторые изменения в класс Programmer (новые строки выделены жирным шрифтом):
Public Class Programmer Inherits Employee Private m_gadget As String
Private m_HowToCallMe As String = "Code guru "
Public Sub NewCByVal theName As String, ByVal curSalary As Decimal)
MyBase.New(theName, curSalary)
m_HowToCal1Me = m_HowToCallMe StheName
End Sub
Public Overloads Overrides Sub RaiseSalary(ByVal Percent As Decimal)
MyBase.RaiseSalary(1.2D * Percent, "special")
End Sub
Public Shadows Readonly Property TheName() As String
Get
Return mJtowToCallMe
End Get
End Property
End Class
А теперь попробуйте запустить новый вариант процедуры Sub Main:
Sub Main()
Dim torn As New Employee('Tom". 50000)
Dim sally As New Programmer("Sally". 150000)
Console.WriteLinetsally.TheName)
Dim ourEmployees(l) As Employee
ourEmployees(0)= tom
ourEmployees(l)= sally
Dim anEmployee As Employee
For Each anEmployee In ourEmployees
anEmployee.RaiseSalary(0.lD)
Console.WriteLinetanEmployee.TheName & "salary now is " &
anEmployee. Salary())
Next
Console. ReadLine()
End Sub
Рис. 5.3. Замещение нарушает работу полиморфных вызовов
Результат показан на рис. 5.3.
Как видно из рисунка, полиморфный вызов перестал работать. Первая строка, выделенная в Sub Main жирным шрифтом, правильно ставит перед именем Sally титул «Code Guru». К сожалению, во второй выделенной строке полиморфизм уже не работает, вследствие чего не вызывается метод TheName производного класса Programmer. Результат — имя выводится без титула. Другими словами, при использовании ключевого слова Shadows обращения к членам объектов осуществляются в соответствии с типом контейнера, в котором хранится объект, а не их фактическим типом (можно сказать, что при использовании ключевого слова Shadows в производном классе метод или свойство становится невиртуальным).