Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»
Шрифт:
Интервал:
Вот описание полностью абстрактного класса Stack;
public abstract class Stack
{
public Stack()
{ }
/// <summary>
/// втолкнуть элемент item в стек
/// </summary>
/// <param name="item"></param>
public abstract void put(int item);
/// <summary>
/// удалить элемент в вершине стека
/// </summary>
public abstract void remove();
/// <summary>
/// прочитать элемент в вершине стека
/// </summary>
public abstract int item();
/// <summary>
/// определить, пуст ли стек
/// </summary>
/// <returns></returns>
public abstract bool IsEmpty();
}
Описание класса содержит только сигнатуры методов класса и их спецификацию, заданную тегами <summary>. Построим теперь одного из потомков этого класса, реализация которого основана на списковом представлении. Класс ListStack будет потомком абстрактного класса Stack и клиентом класса Linkable, задающего элементы списка. Класс Linkable выглядит совсем просто:
public class Linkable
{
public Linkable()
{ }
public int info;
public Linkable next;
}
В нем — два поля и конструктор по умолчанию. Построим теперь класс Liststack;
public class Liststack: Stack
{
public Liststack ()
{
top = new Linkable ();
}
Linkable top;
/// <summary>
/// втолкнуть элемент item в стек
/// </summary>
/// <param name="item"></param>
public override void put (int item)
{
Linkable newitem = new Linkable();
newitem.info = item;
newitem.next = top;
top = newitem;
}
/// <summary>
/// удалить элемент в вершине стека
/// </summary>
public override void remove()
{
top = top.next;
}
/// <summary>
/// прочитать элемент в вершине стека
/// </summary>
public override int item()
{
return(top.info);
}
/// <summary>
/// определить, пуст ли стек
/// </summary>
/// <returns></returns>
public override bool IsEmpty()
{
return(top.next == null);
}
}
Класс имеет одно поле top класса Linkable и методы, наследованные от абстрактного класса Stack. Теперь, когда задано представление данных, нетрудно написать реализацию операций. Реализация операций традиционна для стеков и, надеюсь, не требует пояснений.
Приведу пример работы со стеком:
public void TestStack()
{
ListStack stack = new ListStack();
stack.put (7); stack.put (9);
Console.WriteLine(stack.item());
stack.remove(); Console.WriteLine(stack.item());
stack.put (11); stack.put (13);
Console.WriteLine(stack.item());
stack.remove(); Console.WriteLine(stack.item());
if(!stack.IsEmpty()) stack.remove();
Console.WriteLine(stack.item());
}
В результате работы этого теста будет напечатана следующая последовательность целых: 9, 7, 13, и, 7.
Классы без потомков
Экзотическим, но иногда полезным видом классов являются классы, для которых запрещается строить классы-потомки путем наследования. При создании такого класса нет необходимости в выполнении над классом каких-либо болезненных операций. Вполне достаточно приписать классу модификатор sealed — он и запрещает построение потомков.
19. Интерфейсы. Множественное наследование
Интерфейсы как частный случай класса. Множественное наследование. Проблемы. Множественное наследование интерфейсов. Встроенные интерфейсы. Интерфейсы IComparable, ICIoneable, ISerializable. Поверхностное и глубокое клонирование и сериализация. Сохранение и обмен данными.
Интерфейсы
Слово "интерфейс" многозначное и в разных контекстах оно имеет различный смысл. В данной лекции речь идет о понятии интерфейса, стоящем за ключевым словом interface. В таком понимании интерфейс — это частный случай класса. Интерфейс представляет собой полностью абстрактный класс, все методы которого абстрактны. От абстрактного класса интерфейс отличается некоторыми деталями в синтаксисе и поведении. Синтаксическое отличие состоит в том, что методы интерфейса объявляются без указания модификатора доступа. Отличие в поведении заключается в более жестких требованиях к потомкам. Класс, наследующий интерфейс, обязан полностью реализовать все методы интерфейса. В этом — отличие от класса, наследующего абстрактный класс, где потомок может реализовать лишь некоторые методы родительского абстрактного класса, оставаясь абстрактным классом. Но, конечно, не ради этих отличий были введены интерфейсы в язык С#. У них значительно более важная роль.
Введение в язык частных случаев усложняет его и свидетельствует о некоторых изъянах, для преодоления которых и вводятся частные случаи. Например, введение структур в язык C# позволило определять классы как развернутые типы. Конечно, проще было бы ввести в объявление класса соответствующий модификатор, позволяющий любой класс объявлять развернутым. Но этого сделано не было, а, следуя традиции языка C++, были введены структуры как частный случай классов.
Подробнее о развернутых и ссылочных типах см. лекцию 17.
Интерфейсы позволяют частично справиться с таким существенным недостатком языка, как отсутствие множественного наследования классов. Хотя реализация множественного наследования встречается с рядом проблем, его отсутствие существенно снижает выразительную мощь языка. В языке C# полного множественного наследования классов нет. Чтобы частично сгладить этот пробел, допускается множественное наследование интерфейсов. Обеспечить возможность классу иметь несколько родителей — один полноценный класс, а остальные в виде интерфейсов, — в этом и состоит основное назначение интерфейсов.
Отметим одно важное назначение интерфейсов. Интерфейс позволяет описывать некоторые желательные свойства, которыми могут обладать объекты разных классов. В библиотеке FCL имеется большое число подобных интерфейсов, с некоторыми из них мы познакомимся в этой лекции. Все классы, допускающие сравнение своих объектов, обычно наследуют интерфейс IComparable, реализация которого позволяет сравнивать объекты не только на равенство, но и на "больше", "меньше".
Две стратегии реализации интерфейса
Давайте опишем некоторый интерфейс, задающий дополнительные свойства объектов класса:
public interface IProps
Поделиться книгой в соц сетях:
Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!