Урок 20. Абстрактные классы C#


В двадцатой части учебника по объектно-ориентированному программированию на языке C# исследуется использование абстрактных классов. Это специальные классы, которые предназначены для использования только в качестве базовых классов для наследования. Они не позволяют создавать экземпляры объектов.

Что такое абстрактный класс?

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

Часто при работе с иерархиями наследования наименее специализированный базовый класс не может полностью представлять реальный объект. Например, классам, представляющим кубы, сферы и другие трехмерные объекты, могут быть присвоены свойства и методы, позволяющие вычислить их объем и площадь поверхности. Однако если базовый класс для этих типов представляет общие 3D-объекты, эти вычисления невозможны. Кроме того, может оказаться бессмысленным создавать экземпляр такого класса.

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

Создание абстрактного класса

Чтобы продемонстрировать использование абстрактных классов, мы создадим пример проекта, основанного на классе "ThreeDObject". Этот абстрактный класс будет включать как абстрактные, так и конкретные методы и свойства. Затем мы создадим два конкретных подкласса ThreeDObject для представления кубов и сфер.

Для начала создайте новое консольное приложение с именем "AbstractClassesDemo". Добавьте файл класса в проект и назовите его "ThreeDObject".

Синтаксис

Чтобы объявить класс абстрактным, ключевое слово "abstract" используется в качестве префикса к определению класса. Чтобы изменить класс ThreeDObject на абстрактный, измените стандартное объявление следующим образом:

abstract class ThreeDObject
Теперь, когда класс является абстрактным, невозможно создать экземпляр объекта ThreeDObject. Это можно показать, добавив следующий код к основному методу программы. При попытке компиляции программы возникает ошибка.

ThreeDObject cube = new ThreeDObject();
Создание абстрактного свойства

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

Как и в случае с конкретными свойствами, абстрактные свойства определяются с помощью методов доступа get и set . Если метод доступа set опущен, то свойство будет доступно только для чтения. Для свойства только для записи включен только метод доступа set. Поскольку свойство abstract не может содержать кода, ни для одного из методов доступа не создается блок кода.

В следующем коде показаны примеры абстрактных свойств "только для чтения-записи", "только для чтения" и " только для записи:

public abstract int ReadWriteProperty { get; set; }
public abstract int ReadOnlyProperty { get; }
public abstract int WriteOnlyProperty { set; }
Мы добавим абстрактное свойство к ThreeDObject, чтобы представить площадь поверхности трехмерного объекта. Это свойство только для чтения является хорошим примером абстрактного элемента, поскольку невозможно определить площадь поверхности для общего типа. Однако подклассы, представляющие определенный тип трехмерного объекта, смогут выполнить это вычисление.

Чтобы создать свойство площадь поверхности, добавьте следующий код в класс ThreeDObject:

public abstract double SurfaceArea { get; }
Создание абстрактного метода

Абстрактные методы могут быть созданы только в пределах абстрактных классов. Как и абстрактные свойства, они являются заполнителем для методов, которые должны быть реализованы в подклассах. Для создания абстрактного метода ключевое слово "abstract" используется в качестве префикса к объявлению, и блок кода не создается.

Чтобы создать абстрактный метод в ThreeDObject, который вычисляет объем объекта, добавьте в класс следующий код:

public abstract double CalculateVolume();
Примечание: необычно использовать свойство для вычисления площади поверхности и метода для объема. Этот выбор был сделан только в демонстрационных целях.

Создание конкретных элементов в абстрактном классе

Хотя невозможно создать экземпляр абстрактного класса, можно создать конкретные члены в этом классе. Эти члены будут унаследованы подклассами точно таким же образом, как и при наследовании от конкретных классов. Члены также могут быть переопределены с помощью полиморфизма, если они помечены как виртуальные.

В объекте ThreeDObject мы создадим одно конкретное свойство и конкретный метод. Свойство будет содержать высоту трехмерного объекта, а метод будет вычислять соотношение между объемом объекта и площадью поверхности. Этот расчет может быть выполнен в абстрактном классе с использованием свойства SurfaceArea и метода CalculateVolume, каждый из которых будет определен в любом подклассе.

Добавьте свойство, резервную переменную хранилища и метод в ThreeDObject, используя следующий код:

private double _height;
 
public double Height
{
    get { return _height; }
    set { _height = value; }
}
 
public double CalculateVolumeToAreaRatio()
{
    return CalculateVolume() / SurfaceArea;
}
Наследование от абстрактного класса

Вывод подкласса из абстрактного класса практически идентичен наследованию от любого другого класса. Единственное различие заключается в том, что абстрактные члены базового класса должны быть реализованы.

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

Для начала добавьте в проект два новых файла класса. Создайте один файл класса для класса "Cube" и второй для класса "Sphere". После добавления двух новых классов измените код таким образом, чтобы они наследовались от класса ThreeDObject, добавив двоеточие (:) и имя базового класса к каждому объявлению.

Код для классов должен быть следующим:

// Cube.cs
 
class Cube : ThreeDObject
{
}
 
 
// Sphere.cs
 
class Sphere : ThreeDObject
{
}
Если вы попытаетесь скомпилировать программу, вы получите четыре ошибки компилятора. Они указывают, что абстрактное свойство и метод должны быть реализованы в каждом подклассе.

Реализация абстрактных элементов

Для завершения двух подклассов необходимо определить функциональные возможности абстрактных элементов суперкласса. Это достигается путем переопределения абстрактных элементов с помощью ключевого слова override.

Чтобы вычислить площадь поверхности куба, высота будет возведена в квадрат для вычисления площади одной грани. Затем результат умножается на шесть, чтобы включить все шесть граней. Объем куба - это просто высота в кубе. Чтобы добавить эти вычисления при переопределении абстрактных членов, добавьте следующий код в класс Куба:

public override double SurfaceArea
{
    get { return Math.Pow(Height, 2) * 6; }
}
 
public override double CalculateVolume()
{
    return Math.Pow(Height, 3);
}
Расчеты для сферы немного сложнее, так как они используют значение pi (пи). Это значение может быть получено из класса "Math". Формула для площади поверхности сферы равна 4*pi*r2, где "r" - радиус сферы. В нашем примере радиус равен половине значения свойства Height. Формула для объема сферы равна 4/3*pi*r3.

Чтобы добавить эти вычисления, добавьте следующий код в класс sphere:

public override double SurfaceArea
{
    get { return 4 * Math.PI * Math.Pow(Height / 2, 2); }
}
 
public override double CalculateVolume()
{
    return (4 / 3) * Math.PI * Math.Pow(Height / 2, 3);
}
Тестирование подклассов

Теперь мы можем протестировать два подкласса, используя класс программы и основной метод. Для выполнения проверки абстрактных и конкретных членов базового класса и переопределенных членов классов куба и сферы мы создадим простой статический метод, который выводит все измерения, предоставляемые ThreeDObject. Мы будем вызывать это дважды из основного метода, сначала для куба, а затем для сферы. Вот код, необходимый в классе программы и ожидаемый результат:

static void Main()
{
    Cube cube = new Cube();
    cube.Height = 2.5;
    OutputDimensions("Cube", cube);
 
    Sphere sphere = new Sphere();
    sphere.Height = 2.5;
    OutputDimensions("Sphere", sphere);
}
 
static void OutputDimensions(string name, ThreeDObject object3D)
{
    Console.WriteLine();
    Console.WriteLine(name);
    Console.WriteLine("Height\t{0:f2}m", object3D.Height);
    Console.WriteLine("Area\t{0:f2}m2", object3D.SurfaceArea);
    Console.WriteLine("Volume\t{0:f2}m3", object3D.CalculateVolume());
    Console.WriteLine("V:A\t{0:f2}", object3D.CalculateVolumeToAreaRatio());
}
 
/* 
 
Cube
Height  2.50m
Area    37.50m2
Volume  15.63m3
V:A     0.42
 
Sphere
Height  2.50m
Area    19.63m2
Volume  6.14m3
V:A     0.31
 
*/
Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегистатьи IT, си шарп, ООП




Отправляя сообщение я подтверждаю, что ознакомлен и согласен с политикой конфиденциальности данного сайта.



9 советов для разработчиков JavaScript
Поля и методы в Java: передача параметров по значению аргумента
Урок 12. Рендеринг диаграмм в приложениях Laravel