- •Интерфейсы. Множественное наследование
- •Интерфейсы
- •Две стратегии реализации интерфейса
- •Реализация методов интерфейса как общедоступных методов
- •Реализация методов интерфейса как закрытых методов
- •Множественное наследование и его проблемы
- •Коллизия имен
- •Наследование от общего предка
- •Склеивание и переименование
- •Интерфейсы и поля
- •Встроенные интерфейсы
- •Упорядоченность объектов и интерфейс iComparable
- •Приведение типов и операции as и is
- •Оператор присваивания
- •Порядок на классе Person
- •Клонирование и интерфейс iCloneable
- •Перечислимость объектов и интерфейсы
- •Интерфейс перечислимости iEnumerable
- •Итераторы
- •Интерфейс iEnumerator
Интерфейсы. Множественное наследование
Многозначное слово интерфейс в данной лекции задает еще один важный частный случай класса – класс, заданный заголовками методов. Интерфейсы важны не только потому, что позволяю задать определенное поведение у потомков интерфейса, но и позволяют справиться с проблемой множественного наследования. Лекция сопровождается задачами.
Интерфейсы
Слово " интерфейс " - многозначное, и в разных контекстах оно имеет различный смысл. В данной лекции речь идет о понятии интерфейса, стоящем за ключевым словом interface. В таком понимании интерфейс - это частный случай класса. Интерфейс представляет собой полностью абстрактный класс, все методы которого абстрактны. От абстрактного класса интерфейс отличается некоторыми деталями в синтаксисе и поведении. Синтаксическое отличие состоит в том, что методы интерфейса объявляются без указания модификатора доступа. Отличие в поведении заключается в более жестких требованиях к потомкам. Класс, наследующий интерфейс, обязан полностью реализовать все методы интерфейса. В этом отличие от класса, наследующего абстрактный класс, где потомок может реализовать лишь некоторые методы родительского абстрактного класса, оставаясь абстрактным классом. Но, конечно, не ради этих отличий были введены интерфейсы в язык C#. У них своя важная роль.
Введение в язык частных случаев усложняет его и свидетельствует о некоторых изъянах, для преодоления которых и вводятся частные случаи. Например, введение структур в язык C# позволило определять классы как развернутые типы. Конечно, проще было бы ввести в объявление класса соответствующий модификатор, позволяющий любой класс объявлять развернутым. Но этого сделано не было.
Интерфейсы позволяют частично справиться с таким существенным недостатком языка, как отсутствие множественного наследования классов. Хотя реализация множественного наследования сталкивается с рядом проблем, его отсутствие существенно снижает выразительную мощь языка. В языке C# полного множественного наследования классов нет. Чтобы частично сгладить этот пробел, допускается множественное наследование интерфейсов. Обеспечить возможность классу иметь несколько родителей - один полноценный класс, а остальные в виде интерфейсов, - в этом и состоит основное назначение интерфейсов.
Отметим еще одно важное назначение интерфейсов, отличающее их от абстрактных классов. Абстрактный класс представляет собой начальный этап проектирования класса, который в будущем получит конкретную реализацию. Интерфейсы задают дополнительные свойства класса. Один и тот же интерфейс позволяет описывать свойства, которыми могут обладать разные классы. Они похожи на сказочных фей, каждая из которых наделяет принцессу при ее рождении тем или иным свойством. В библиотеке FCL имеется большое число интерфейсов, наследуя которые, классы получают полезные дополнительные свойства. С некоторыми из них мы познакомимся в этой лекции.
Две стратегии реализации интерфейса
Давайте опишем некоторый интерфейс, реализация методов которого позволит классу проводить некоторые преобразования над объектами класса и возвращать строки, представляющие результат этих преобразований. Пример немного искусственный, поскольку наша главная задача сейчас состоит в том, чтобы рассмотреть, как класс реализует методы наследуемого интерфейса.
interface IStrings
{
/// <summary>
/// Преобразование
/// </summary>
/// <returns>результат преобразования</returns>
string Convert();
/// <summary>
/// Шифрование
/// </summary>
/// <param name="code">код шифра</param>
/// <returns>результат шифрования</returns>
string Cipher(string[] code);
}
У этого интерфейса два метода, которые и должны будут реализовать все классы, наследники интерфейса. Как следует из спецификации, заданной в тегах summary, метод Convert должен, следуя алгоритму, выбранному потомком, преобразовать объект, возвращая строку, а метод Chiper, возвращающий строку, рассматривается как шифрование, в алгоритме которого используется массив строк code, переданный методу.