Чтение онлайн

ЖАНРЫ

C# 4.0 полное руководство - 2011

Шилдт Герберт

Шрифт:

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);

}

I/ Класс для прямоугольников, производный от класса 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 IsSquareO {

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# имеется. Он состоит в использовании абстрактного метода.

Поделиться с друзьями: