🏆 151 курс за 1 подписку: хватит выбирать — бери все и сразу!

Один клик — 151 возможность. Подпишись на OTUS сейчас!
Техномир мчится вперед, а вместе с ними растут и требования к специалистам. OTUS придумал крутую штуку — подписку на 151 курс по всем ключевым направлениям IT!
-
Почему подписка OTUS меняет правила игры:
- Доступ к 151 курсу от практикующих экспертов
- В 3 раза выгоднее, чем покупать каждый курс отдельно
- До 3 курсов одновременно без дополнительных затрат
- Свобода выбора направления — меняй треки когда угодно
Изучай новое, развивайся в своем темпе, меняй направления — подпишись на OTUS и прокачивай скилы по полной!
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576. Erid 2VtzqupFnNL
Абстрактные классы
Абстрактный класс похож на обычный класс. Он также может иметь переменные, методы, конструкторы, свойства. Единственное, что при определении абстрактных классов используется ключевое слово abstract
.
Модификатор abstract
:
- указывает, что реализация сущности с данным модификатором является неполной или отсутствует.
- может использоваться с классами, методами, свойствами, индексаторами и событиями.
- в объявлении класса указывает, что класс предназначен только для использования в качестве базового класса для других классов.
Члены, помеченные как абстрактные или включенные в абстрактный класс, должны быть реализованы с помощью классов, производных от абстрактных классов.
Например, определим абстрактный класс, который представляет некое транспортное средство:
abstract class Transport
{
public void Move()
{
Console.WriteLine("Транспортно средство движется");
}
}
Транспортное средство представляет некоторую абстракцию, которая не имеет конкретного воплощения. Например, есть легковые и грузовые машины, самолеты, морские суда, но как такового транспортного средства нет. Тем не менее все транспортные средства имеют нечто общее – они могут перемещаться. И для этого в классе определен метод Move
, который эмулирует перемещение.
Но главное отличие абстрактных классов от обычных состоит в том, что мы НЕ можем использовать конструктор абстрактного класса для создания экземпляра класса. Например, следующим образом:
Transport tesla = new Transport();
Тем не менее абстрактные классы полезны для описания некоторого общего функционала, который могут наследовать и использовать производные классы:
Transport ship = new Ship();
Transport aircraft = new Aircraft();
car.Move();
ship.Move();
aircraft.Move();
abstract class Transport
{
public void Move()
{
Console.WriteLine("Транспортное средство движется");
}
}
// класс корабля
class Ship : Transport { }
// класс самолета
class Aircraft : Transport { }
// класс машины
class Car : Transport { }
В данном случае от класса Transport
наследуются три класса, которые представляют различные типы транспортных средств. Тем не менее они имеют общую черту – они могут перемещаться с помощью метода Move()
.
Мы не можем использовать конструктор абстрактного класса для создания экземпляра этого класса. Тем не менее такой класс также может определять конструкторы:
Transport car = new Car("машина");
Transport ship = new Ship("корабль");
Transport aircraft = new Aircraft("самолет");
car.Move(); // машина движется
ship.Move(); // корабль движется
aircraft.Move(); // самолет движется
abstract class Transport
{
public string Name { get; }
// конструктор абстрактного класса Transport
public Transport(string name)
{
Name = name;
}
public void Move() =>Console.WriteLine($"{Name} движется");
}
// класс корабля
class Ship : Transport
{
// вызываем конструктор базового класса
public Ship(string name) : base(name) { }
}
// класс самолета
class Aircraft : Transport
{
public Aircraft(string name) : base(name) { }
}
// класс машины
class Car : Transport
{
public Car(string name) : base(name) { }
}
В данном случае в абстрактном классе Transport
определен конструктор – с помощью параметра он устанавливает значение свойства Name
, которое хранит название транспортного средства. И в этом случае производные классы должны в своих конструкторах вызвать этот конструктор.
Абстрактные члены классов
Кроме обычных свойств и методов, абстрактный класс может иметь абстрактные члены классов, которые определяются с помощью ключевого слова abstract
и не имеют никакого функционала. В частности, абстрактными могут быть:
- Методы.
- Свойства.
- Индексаторы.
- События.
Абстрактные члены классов не должны иметь модификатор private
. При этом производный класс обязан переопределить и реализовать все абстрактные методы и свойства, которые имеются в базовом абстрактном классе. При переопределении в производном классе такой метод или свойство также объявляются с модификатором override
(как и при обычном переопределении виртуальных методов и свойств). Также следует учесть, что если класс имеет хотя бы один абстрактный метод (или абстрактные свойство, индексатор, событие), то этот класс должен быть определен как абстрактный.
Абстрактные члены так же, как и виртуальные, являются частью полиморфного интерфейса. Но если в случае с виртуальными методами мы говорим, что класс-наследник наследует реализацию, то в случае с абстрактными методами наследуется интерфейс, представленный этими абстрактными методами.
Абстрактные методы
В примере с транспортными средствами метод Move
описывает передвижение транспортного средства. Однако различные типы транспорта перемещаются по-разному – едут по земле, летят по воздуху, плывут по воде и т. д. В этом случае мы можем сделать метод Move
абстрактным, а его реализацию переложить на производные классы:
abstract class Transport
{
public abstract void Move();
}
// класс корабля
class Ship : Transport
{
// мы должны реализовать все абстрактные методы и свойства базового класса
public override void Move()
{
Console.WriteLine("Корабль плывет");
}
}
// класс самолета
class Aircraft : Transport
{
public override void Move()
{
Console.WriteLine("Самолет летит");
}
}
// класс машины
class Car : Transport
{
public override void Move()
{
Console.WriteLine("Машина едет");
}
}
Transport car = new Car();
Transport ship = new Ship();
Transport aircraft = new Aircraft();
car.Move(); // машина едет
ship.Move(); // корабль плывет
aircraft.Move(); // самолет летит
Абстрактные свойства
Следует отметить использование абстрактных свойств. Их определение похоже на определение автосвойств. Например:
abstract class Transport
{
// абстрактное свойство для хранения скорости
public abstract int Speed { get; set; }
}
// класс корабля
class Ship: Transport
{
int speed;
public override int Speed
{
get => speed;
set => speed = value;
}
}
class Aircraft : Transport
{
public override int Speed { get; set; }
}
В классе Transport
определено абстрактное свойство Speed
, которое должно хранить скорость транспортного средства. Оно похоже на автосвойство, но это не автосвойство. Так как данное свойство не должно иметь реализацию, то оно имеет только пустые блоки get
и set
. В производных классах мы можем переопределить это свойство, сделав его полноценным свойством (как в классе Ship
), либо же сделав его автоматическим (как в классе Aircraft
).
Отказ от реализации абстрактных членов
Производный класс обязан реализовать все абстрактные члены базового класса. Однако мы можем отказаться от реализации, но в этом случае производный класс также должен быть определен как абстрактный:
Transport tesla = new Auto();
tesla.Move(); // легковая машина едет
abstract class Transport
{
public abstract void Move();
}
// класс машины
abstract class Car :Transport{}
class Auto: Car
{
public override void Move()
{
Console.WriteLine("легковая машина едет");
}
}
Пример абстрактного класса
Примером является система геометрических фигур. В реальности не существует геометрической фигуры как таковой. Есть круг, прямоугольник, квадрат, но просто фигуры нет. Однако же и круг, и прямоугольник имеют что-то общее и являются фигурами:
// абстрактный класс фигуры
abstract class Shape
{
// абстрактный метод для получения периметра
public abstract double GetPerimeter();
// абстрактный метод для получения площади
public abstract double GetArea();
}
// производный класс прямоугольника
class Rectangle : Shape
{
public float Width { get; set; }
public float Height { get; set; }
// переопределение получения периметра
public override double GetPerimeter() => Width * 2 + Height * 2;
// переопрелеление получения площади
public override double GetArea() => Width * Height;
}
// производный класс окружности
class Circle : Shape
{
public double Radius { get; set; }
// переопределение получения периметра
public override double GetPerimeter() => Radius * 2 * 3.14;
// переопрелеление получения площади
public override double GetArea() => Radius * Radius * 3.14;
}
var rectanle = new Rectangle { Width = 20, Height = 20 };
var circle = new Circle { Radius = 200 };
PrintShape(rectanle); // Perimeter: 80 Area: 400
PrintShape(circle); // Perimeter: 1256 Area: 125600
void PrintShape(Shape shape)
{
Console.WriteLine($"Perimeter: {shape.GetPerimeter()} Area: {shape.GetArea()}");
}
Комментарии