Иллюстрированный самоучитель по 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


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