Шрифт:
Интервал:
Закладка:
public double Width {
get { return pri_width; }
set { pri_width = value < 0 ? -value : value; }
}
public double Height {
get { return pri_height; }
set { pri_height = value < 0 ? -value : value; }
}
public string name { get; set; }
public void ShowDim() {
Console.WriteLine("Ширина и высота равны " +
Width + " и " + Height);
}
public virtual double Area() {
Console.WriteLine("Метод Area() должен быть переопределен");
return 0.0;
}
}
// Класс для треугольников, производный от класса TwoDShape.
class Triangle : TwoDShape {
string Style;
// Конструктор, используемый по умолчанию,
public Triangle() {
Style = "null";
}
// Конструктор для класса Triangle,
public Triangle(string s, double w, double h)
: base (w, h, "треугольник") {
Style = s;
}
//Сконструировать равнобедренный треугольник,
public Triangle(double x) : base(x, "треугольник") {
Style = "равнобедренный";
}
// Сконструировать копию объекта типа Triangle,
public Triangle(Triangle ob) : base(ob) {
Style = ob.Style;
}
// Переопределить метод Area() для класса Triangle,
public override double Area() {
return Width * Height / 2;
}
// Показать тип треугольника,
public void ShowStyle() {
Console.WriteLine("Треугольник " + Style);
}
}
// Класс для прямоугольников, производный от класса TwoDShape.
class Rectangle : TwoDShape {
// Конструктор для класса Rectangle,
public Rectangle(double w, double h)
: base (w, h, "прямоугольник") { }
// Сконструировать квадрат,
public Rectangle(double x) : base(x, "прямоугольник") { }
// Сконструировать копию объекта типа Rectangle,
public Rectangle(Rectangle ob) : base(ob) { }
// Возвратить логическое значение true, если
// прямоугольник окажется квадратом,
public bool IsSquare() {
if(Width == Height) return true;
return false;
}
// Переопределить метод Area() для класса Rectangle,
public override double Area() {
return Width * Height;
}
}
class DynShapes {
static void Main() {
TwoDShape[] shapes = new TwoDShape[5] ;
shapes[0] = new Triangle("прямоугольный", 8.0, 12.0);
shapes[1] = new Rectangle(10);
shapes[2] = new Rectangle(10, 4);
shapes[3] = new Triangle(7.0);
shapes[4] = new TwoDShape(10, 20, "общая форма");
for (int i=0; i < shapes.Length; i++) {
Console.WriteLine("Объект — " + shapes[i].name);
Console.WriteLine("Площадь равна " + shapes[i].Area());
Console.WriteLine();
}
}
}
При выполнении этой программы получается следующий результат.
Объект — треугольник
Площадь равна 48
Объект — прямоугольник
Площадь равна 100
Объект — прямоугольник
Площадь равна 40
Объект — треугольник
Площадь равна 24.5
Объект — общая форма
Метод Area() должен быть переопределен
Площадь равна 0
Рассмотрим данный пример программы более подробно. Прежде всего, метод Area() объявляется как virtual в классе TwoDShape и переопределяется в классах Triangle и Rectangle по объяснявшимся ранее причинам. В классе TwoDShape метод Area() реализован в виде заполнителя, который сообщает о том, что пользователь данного метода должен переопределить его в производном классе. Каждое переопределение метода Area() предоставляет конкретную его реализацию, соответствующую типу объекта, инкапсулируемого в производном классе. Так, если реализовать класс для эллипсов, то метод Area() должен вычислять площадь эллипса.
У программы из рассматриваемого здесь примера имеется еще одна примечательная особенность. Обратите внимание на то, что в методе Main() двумерные формы объявляются в виде массива объектов типа TwoDShape, но элементам этого массива присваиваются ссылки на объекты классов Triangle, Rectangle и TwoDShape. И это вполне допустимо, поскольку по ссылке на базовый класс можно обращаться к объекту прризводного класса. Далее в программе происходит циклическое обращения к элементам данного массива для вывода сведений о каждом объекте. Несмотря на всю свою простоту, данный пример наглядно демонстрирует преимущества наследования и переопределения методов. Тип объекта, хранящийся в переменной ссылки на базовый класс, определяется во время выполнения и соответственно обусловливает дальнейшие действия. Так, если объект является производным от класса TwoDShape, то для получения его площади вызывается метод Area(). Но интерфейс для выполнения этой операции остается тем же самым независимо от типа используемой двумерной формы.
Применение абстрактных классов
Иногда требуется создать базовый класс, в котором определяется лишь самая общая форма для всех его производных классов, а наполнение ее деталями предоставляется каждому из этих классов. В таком классе определяется лишь характер методов, которые должны быть конкретно реализованы в производных классах, а не в самом базовом классе. Подобная ситуация возникает, например, в связи с невозможностью получить содержательную реализацию метода в базовом классе. Именно такая ситуация была продемонстрирована в варианте класса TwoDShape из предыдущего примера, где метод Area() был просто определен как заполнитель. Такой метод не вычисляет и не выводит площадь двумерного объекта любого типа.
Создавая собственные библиотеки классов, вы можете сами убедиться в том, что у метода зачастую отсутствует содержательное определение в контексте его базового класса. Подобная ситуация разрешается двумя способами. Один из них, как показано в предыдущем примере, состоит в том, чтобы просто выдать предупреждающее сообщение. Такой способ может пригодиться в определенных ситуациях, например при отладке, но в практике программирования он обычно не применяется. Ведь в базовом классе могут быть объявлены методы, которые должны быть переопределены в производном классе, чтобы этот класс стал содержательным. Рассмотрим для примера класс Triangle. Он был бы неполным, если бы в нем не был переопределен метод Area(). В подобных случаях требуется какой-то способ, гарантирующий, что в производном классе действительно будут переопределены все необходимые методы. И такой способ в C# имеется. Он состоит в использовании абстрактного метода.
Абстрактный метод создается с помощью указываемого модификатора типа abstract. У абстрактного метода отсутствует тело, и поэтому он не реализуется в базовом классе. Это означает, что он должен быть переопределен в производном классе, поскольку его вариант из базового класса просто непригоден для использования. Нетрудно догадаться, что абстрактный метод автоматически становится виртуальным и не требует указания модификатора virtual. В действительности совместное использование модификаторов virtual и abstract считается ошибкой.
Для определения абстрактного метода служит приведенная ниже общая форма.
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- ИНФОРМАЦИОННАЯ ТЕХНОЛОГИЯ. РУКОВОДСТВО ПО УПРАВЛЕНИЮ ДОКУМЕНТИРОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ - ГОССТАНДАРТ РОССИИ - Программирование
- Управление исходными текстами. Часть 1. Краткое руководство по CVS - Илья Рыженков - Программирование
- Гибкое управление проектами и продуктами - Борис Вольфсон - Программирование
- Каждому проекту своя методология - Алистэр Коуберн - Программирование
- Разработка ядра Linux - Роберт Лав - Программирование
- Как спроектировать современный сайт - Чои Вин - Программирование
- Творческий отбор. Как создавались лучшие продукты Apple во времена Стива Джобса - Кен Косиенда - Прочая околокомпьтерная литература / Интернет / Программирование
- Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT. Часть 2 - Александр Фролов - Программирование