Шаг 156.
Язык программирования C#. Начала.
Абстрактные классы и интерфейсы. Знакомство с интерфейсами

    На этом шаге мы приведем общие сведения об интерфейсах.

    На предыдущих шагах мы познакомились с базовыми принципами использования абстрактных классов. Как уже несколько раз отмечалось, использование абстрактного класса в качестве базового позволяет создавать производные классы "по одному шаблону" - то есть с одинаковым набором свойств и методов. Вместе с тем здесь имеется один "тонкий момент". Дело в том, что в языке C# запрещено множественное наследование: мы не можем создать производный класс на основе сразу нескольких базовых.


Множественное наследование есть в языке C++. В языке C++ у производного класса может быть несколько базовых классов. В языках Java и C# от множественного наследования отказались в целях безопасности.

    Это не очень хорошо, поскольку часто возникает необходимость "объединить в одно целое" сразу несколько классов. Например, в языке C++, ставшем "прародителем" для языка C#, такая возможность существует. Это полезная возможность, но одновременно это и небезопасная возможность. Ведь разные классы описывались независимо друг от друга. Их объединение в один класс может привести к конфликтным ситуациям. Поэтому в языке C# от технологии множественного наследования отказались. Вместо множественного наследования используется другая технология, связанная с реализацией интерфейсов.

    Главная опасность в попытке объединения классов связана с тем, что в них есть методы и эти методы каким-то образом определены. Когда метод описывался в классе, перспектива совместного использования этого метода с методами из иных классов, скорее всего, не рассматривалась. Отсюда и неприятные сюрпризы. Но если объединять классы с абстрактными методами, то данная проблема снимается автоматически, поскольку абстрактные методы не имеют тела, они только объявлены (но не описаны). Необходимо только обеспечить, чтобы все методы были абстрактными. В обычном абстрактном классе в общем случае это не так. Отсюда появляется потребность в интерфейсах.

    Интерфейс представляет собой блок из абстрактных методов, свойств и индексаторов. Фактически это аналог абстрактного класса. Но, в отличие от абстрактного класса, в интерфейсе абсолютно все абстрактное. Описывается интерфейс специальным образом, хотя описание интерфейса и напоминает описание класса. Общий шаблон описания интерфейса представлен ниже:

  interface имя {
      // Тело интерфейса
  }

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


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

    Для методов указывается только сигнатура: тип результата, название метода и список аргументов. Ключевое слово abstract не указывается, как и ключевое слово virtual. По умолчанию объявленные в интерфейсе методы (а также свойства и индексаторы) считаются абстрактными и виртуальными. Спецификатор уровня доступа также не указывается. Все методы (свойства, индексаторы), объявленные в интерфейсе, являются открытыми (то есть будто бы описанными с ключевым словом public, хотя оно явно не используется).


Свойства и индексаторы в интерфейсе объявляются с пустым телом, в котором указываются ключевые слова get и set. Наличие обоих ключевых слов (после каждого ставится точка с запятой) означает, что при описании у свойства и индексатора должно быть два аксессора. Можно указывать только одно ключевое слово, соответствующее аксессору, который должен быть у свойства или индексатора.

    Интерфейс нужен для того, чтобы на его основе создавать классы. Если класс создается на основе интерфейса, то говорят, что класс реализует интерфейс. Реализация интерфейса в классе подразумевает, что в этом классе описаны все методы, свойства и индексаторы, которые объявлены в интерфейсе. Причем при описании методов, свойств и индексаторов в классе ключевое слово override не используется.


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

    Имя интерфейса, реализуемого в классе, указывается в описании класса через двоеточие после имени класса (то есть так же, как указывается имя базового класса при наследовании). Шаблон описания класса, реализующего интерфейс, следующий:

  class имя: интерфейс {
    // Тело класса
  }

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

  class имя: интерфейс1, интерфейс2, ..., интерфейсN {
    // Тело класса
  }

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

  class имя: базовый класс, интерфейс1, интерфейс2, ..., интерфейсN {
    // Тело класса
  }

    Если так, то в классе должны быть описаны все методы, свойства и индексаторы, объявленные в реализуемых интерфейсах, а если наследуемый базовый класс абстрактный - то и все абстрактные методы из базового класса.

    На следующем шаге мы продолжим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг