Одним из ключевых аспектов объектно-ориентированного программирования является наследование (inheritance). Сущность наследования заключается в том, что мы можем расширить функционал уже имеющихся классов с помощью создания наследников этого класса. Допустим, у нас определен следующий класс Person, описывающий отдельного человека:
Public Class Person Dim _firstName As String Dim _lastName As String Public Property FirstName() As String Get Return _firstName End Get Set(value As String) _firstName = value End Set End Property Public Property LastName() As String Get Return _lastName End Get Set(value As String) _lastName = value End Set End Property Public Sub Display() Console.WriteLine(FirstName & " " & LastName) End Sub End Class
Теперь создадим класс, описывающий сотрудника предприятия - класс Employee. Поскольку этот класс будет реализовывать тот же функционал, что и класс Person, так как сотрудник - это также и человек, то было бы рационально сделать класс Employee производным (или наследником) от класса Person, который, в свою очередь, называется базовым классом или родителем. Чтобы наследовать одни класс от другого, нужно использовать ключевое слово Inherits:
Public Class Employee Inherits Person End Class
По умолчанию все классы могут наследоваться. Однако здесь есть ряд ограничений:
Во-первых, не поддерживается двойное наследование, класс может наследоваться только от одного класса. Хотя проблема множественного наследования реализуется с помощью концепции интерфейсов, о которых мы поговорим позже.
Во-вторых, при создании производного класса мы должны учитывать тип доступа к базовому классу - тип доступа к производному классу должен быть таким же, как и у базового класса, или более строгим. То есть если базовый класс у нас имеет тип доступа Friend, то производный класс может иметь тип доступа Friend или Private, но не Public.
В-третьих, если класс объявлен с модификатором NotInheritable, то от этого класса нельзя наследовать и создавать производные классы. Например, следующий класс не допускает создание наследников:
Public NotInheritable Class Person End Class
И в этом случае создать от этого класса класс Employee как и любой другой мы не сможем.
Но вернемся к нашему классу Employee. Поскольку он образован от класса Person, то он обладает той же функциональностью, что определена в классе
Person. Однако посмотрим, что будет, если мы захотим создать метод для вывода на экран имени сотрудника, которое храниться в переменной _firstName
:
Public Class Employee Inherits Person Public Sub DisplayFirstName() Console.WriteLine(_firstName) End Sub End Class
Этот код у нас работать не будет, так как переменная _firstName
определена как Private(как вы помните, модификатор Dim
аналогичен модификатору Private
), поэтому к ней может иметь доступ только класс Person
. Но зато в классе Person
определено общедоступное свойство FirstName
, которое мы можем использовать, поэтому следующий код у нас будет работать нормально:
Public Class Employee Inherits Person Public Sub DisplayFirstName() Console.WriteLine(FirstName) End Sub End Class
Таким образом, производный класс может иметь доступ только к тем членам базового класса, которые определены с модификаторами Public, Friend, Protected и Protected Friend.
Итак, мы уже можем использовать наши классы в программе:
Sub Main() Dim emp As New Employee() emp.FirstName = "Bill" emp.LastName = "Gates" emp.Display() Console.ReadLine() End Sub
Но что если мы захотим добавить в класс Employee
свойство, представляющее компанию, в которой сотрудник работает, и выводить
в методе Display
сведения о компании. То есть мы хотим переопределить функционал метода Display
в производном классе.
Для этого нам надо в классе Person
пометить метод Display
модификатором Overridable. А уже в производном классе
этот же метод использовать с модификатором Overrides:
'Базовый класс Person Public Class Person 'Здесь остальной код ..... Public Overridable Sub Display() Console.WriteLine(FirstName & " " & LastName) End Sub End Class 'Производный класс Employee Public Class Employee Inherits Person Public Property Company As String Public Overrides Sub Display() Console.WriteLine(FirstName & " " & LastName & " works in " & Company) End Sub End Class
Чтобы запретить переопределение, вместо Overridable надо использовать ключевое слово NotOverridable (оно используется по умолчанию, если не указано Overridable).
Следует сказать, что мы могли обойтись в нашем примере и без переопределения. Мы могли просто скрыть метод базового класса и объявить в производном классе новый метод с таким же именем. Для этого в объявлении метода в производном классе надо использовать ключевое слово Shadows:
'Базовый класс Person Public Class Person 'Здесь остальной код ..... Public Sub Display() Console.WriteLine(FirstName & " " & LastName) End Sub End Class 'Производный класс Employee Public Class Employee Inherits Person Public Property Company As String Public Shadows Sub Display() Console.WriteLine(FirstName & " " & LastName & " works in " & Company) End Sub End Class
И если мы вызовем метод Display
у объекта класса Employee
, то будет использоваться именно метод с модификатором Shadows:
Sub Main() Dim emp As New Employee() emp.FirstName = "Bill" emp.LastName = "Gates" emp.Company = "Microsoft" emp.Display() 'Здесь будет выведено 'Bill Gates works in Microsoft' Console.ReadLine() End Sub
При этом важно помнить, что Shadows не переопределяет метод базового класса, а лишь скрывает его.
Теперь добавим в наши классы конструкторы: один конструктор будет без параметров и один с параметрами:
'Базовый класс Person Public Class Person 'Здесь остальной код ..... 'Конструкторы класса Public Sub New() End Sub Public Sub New(fName As String, lName As String) FirstName = fName LastName = lName End Sub End Class 'Производный класс Employee Public Class Employee Inherits Person 'Здесь остальной код ..... 'Конструкторы класса Public Sub New(fName As String, lName As String, comp As String) MyBase.New(fName, lName) Company = comp End Sub Public Sub New() End Sub End Class
В конструктор с параметрами мы передаем значения для свойств класса. В конструкторе класса Person
мы устанавливаем имя и фамилию
объекта, а в конструкторе класса Employee
- имя, фамилию и компанию. Поскольку при создании объекта класса вызываются все конструкторы его
родительских классов, то есть при создании объекта класса Employee
:
Dim emp As New Employee()
Сначала вызывается конструктор без параметров класса Person
, а затем уже конструктор без параметров класса Employee
.
Поэтому нам нет смысла устанавливать все свойства в конструкторе Employee
. И мы передаем часть значений в конструктор родительского класса
с помощью ключевого слова MyBase. Это слово используется для получения членов родительского класса. В данном случае мы получили доступ к конструктору.