Cамоучитель по VB.NET

Механика реализации интерфейса


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

В описанной выше иерархии классов VB .NET определить новый класс «ведущий специалист» не удастся, поскольку классы Programmer и Tester уже являются производными от класса Empl oyee, а множественное наследование в .NET не поддерживается. Перед нами идеальный пример ситуации, когда вам на помощь приходят интерфейсы.
По общепринятым правилам имена интерфейсов в .NET начинаются с прописной бук-вы «I», поэтому в следующем примере интерфейс называется ILead.

Прежде всего интерфейс необходимо определить. В отличие от VB6, где интерфейс был обычным классом, в VB .NET появилось специальное ключевое слово Interface. Предположим, наши «ведущие» должны оценивать своих подчиненных и тратить средства из фонда материального поощрения. Определение интерфейса выглядит так:

Public Interface ILead

Sub SpendMoraleFund(ByVal amount As Decimal)

Function Rate(ByVal aPerson As Employee) As String

Property MyTeam() As Empl oyee ()

Property MoraleFuod() As Decimal End Interface

Обратите внимание — в определении интерфейса отсутствуют модификаторы уровня доступа Publiс и Private. Разрешены только объявления Sub, Function и Property с ключевыми словами Overloads и Default. Как видите, определение интерфейса выглядит просто. Любой класс, реализующий интерфейс ILead, обязуется содержать:

  • процедуру с параметром типа Decimal;
  • функцию, которая получает объект Empl oyee и возвращает строку;
  • свойство, доступное для чтения и записи, возвращающее массив объектов Employee;
  • свойство, доступное для чтения и записи, возвращающее значение типа Decimа1.

Как будет показано ниже, имена методов реализации несущественны — главное, чтобы методы имели заданную сигнатуру.

Чтобы реализовать интерфейс в классе, прежде всего убедитесь в том, что он сам или ссылка на него входит в проект. Далее за именем класса и командой Inherits в программу включается строка с ключевым словом Implements, за которым следует имя интерфейса. Пример:

Public Class LeadProgrammer

Inherits Programmer

Implements Head

End Class

Имя Head подчеркивается синей волнистой чертой, свидетельствующей о возникшей проблеме. Тем самым компилятор настаивает на выполнении обязательств по реализации интерфейса хотя бы пустыми методами.

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

Public Function Rate(ByVal aPerson As Employee) As String _

Implements ILead.Rate End Function

Конечно, имена членов интерфейса обычно совпадают с именами методов, их реализующих, но это не обязательно. Например, следующий фрагмент вполне допустим.

Public Property OurMoraleFund() As Decimal Implements

Head.MoraleFund Get

Return m_Moral e Fund

End Get

Set(ByVal Value As Decimal)

m_MoraleFund =Value

End Set

End Property

Главное, чтобы типы параметров и возвращаемого значения соответствовали сигнатуре данного члена интерфейса. При объявлении метода могут использоваться любые допустимые модификаторы, не мешающие выполнению контракта, — Overloads, Overrides, Overridable, Public, Private, Protected, Friend, Protected Friend, MustOverride, Default и Static. Запрещается только использовать атрибут Shared, поскольку члены интерфейса должны принадлежать конкретному экземпляру, а не классу в целом.

Если в реализации интерфейса используется член класса с модификатором Pri vate, обращения к этому члену возможны только через переменную, объявленную с типом данного интерфейса. В отличие от предыдущих версий VB в остальных случаях к членам интерфейса всегда можно обращаться через объекты класса. Теперь вам не придется присваивать их промежуточным интерфейсным переменным. Пример:

Dim tom As New LeadProgrammer("Tom",65000) tom.SpendMoraleFund(500)

Однако в обратных преобразованиях приходится использовать функцию СТуре:

Dim tom As New LeadProgrammer("Tom". 65000)

Dim aLead As ILead.aName As String

aLead = tom

aName = Ctype(aLead. Programmer).TheName 'OK

Следующая строка недопустима:

aName =tom.TheName ' ЗАПРЕЩЕНО!

Ниже перечислены общие правила преобразования между типом объекта и интерфейсом, им реализуемым.

  • Переменную, объявленную с типом класса, всегда можно присвоить переменной, объявленной с типом любого из интерфейсов, реализуемых классом. В частности, если метод получает в качестве параметра переменную интерфейсного типа, ему можно передать переменную любого из типов, реализующих этот интерфейс (данное правило напоминает основной принцип наследования, в соответствии с которым производные типы всегда могут использоваться вместо базовых). Запомните следующее правило:
  • При переходе от переменной типа интерфейса к переменной типа, реализующего интерфейс, необходимо использовать функцию СТуре.

Чтобы определить, реализует ли объект некоторый интерфейс, воспользуйтесь ключевым словом TypeOf в сочетании с Is. Пример:

Dim torn As New LeadProgrammer("tom". 50000)

Console.WriteLine((TypeOf (tom) Is Head))

Вторая строка выводит значение True.

Один метод может реализовывать несколько функций, определенных в одном интерфейсе:

Public Sub itsOK Implements

Interface1.Ml.Interfacel.M2,Interfacel.M3

Ниже приведена полная версия класса LeadProgrammer. Конечно, реализация методов интерфейса выглядит несколько условно, однако опадает представление о том, что можно сделать при реализации интерфейса:

Public Class LeadProgrammer

Inherits Programmer Implements Head

Private m_MoraleFund As Decimal

Private m_MyTeam As Employee()

Public Function Rate(ByVal aPerson As Employee) As String _

Implements Head.Rate

Return aPerson.TheName & "rating to be done"

End Function

Public Property MyTeam() As Employee()

Implements ILead.MyTeam

Get

Return m_MyTeam

End Get

SeUByVal Value As Employee()) X.

m_MyTeam = Value

End Set End Property

Public Sub SpendMoraleFund(ByVal

amount As Decimal)_

Implements ILead.SpendMocaleFund

' Израсходовать средства из фонда мат. поощрения

Console.WriteLine("Spent " & amount.ToString())

End Sub

Public Property OurMoraleFund()As Decimal

Implements ILead.MoraleFund

Get

Return m_MoraleFund

End Get

SettByVal Value As Decimal)

m_MoraleFund = Value

End Set End Property

Public Sub New(ByVal theName As String. ByVal curSalary As Decimal)

MyBase.New(theName. curSalary)

End Sub

End Class





Содержание раздела