Наследование как средство усложнения базового класса
Реализуется путем определения в классе-потомке дополнительных полей, методов и свойств. Класс-потомок будет иметь:
• Унаследованные поля, методы и свойства
• Дополнительные поля, методы и свойства
Конструкторы не наследуются, поэтому класс-потомок должен иметь собственный конструктор. При создании объекта класса-потомка конструкторы должны вызываться, начиная с базового класса. В начале инициализируются унаследованные поля, потом – специфические поля, как показано на Рис.2.
Указанная очередность вызовов реализуется одним из способов:
1. Конструктор потомка не имеет вызова конструктора-предка. Автоматически вызывается конструктор предка без параметров.
2. Конструктор потомка явно вызывает конструктор предка.
|
Пример: определить базовый класс «Человек». Человек характеризуется фамилией и умением сообщать свою фамилию. Потомками класса «Человек» являются:
-класс «Владелец», дополнительно характеризующийся номером автомобиля и умением сообщать его
-класс “Служащий”, дополнительно характеризующийся названием фирмы и умением сообщать его.
-класс “Студент”, дополнительно характеризующийся названием ВУЗа
class Человек
{
protected string фам;
public Человек(string фам)
{
this.фам = фам;
}
public void Показать()
{
Console.WriteLine("Я - человек: " + фам);
}
}
class Студент:Человек
{
private string вуз;
public Студент(string фам, string вуз):base(фам)
{
this.вуз = вуз;
}
}
class Владелец: Человек
{
private string ном;
public Владелец(string фам, string ном): base(фам)
{
this.ном = ном;
}
public void Инфо()
{
Console.WriteLine("Я - владелец: "+фам+" --> " + ном);
}
}
class Служащий: Человек
{
private string фирма;
public Служащий(string фам, string фирма): base(фам)
{
this.фирма = фирма;
}
public void Инфо()
{
Console.WriteLine("Я - служащий: "+фам + "____" + фирма);
}
}
class Program
{
static void Main(string[] args)
{
Студент ст = new Студент("Иванов","ВШЭ");
Владелец вл = new Владелец("Петров", "A777AA-99RUS");
Служащий сл = new Служащий("Сидоров", "Рога и копыта");
ст.Показать(); //Унаследованный метод
//Я - человек: Иванов
вл.Показать(); //Унаследованный метод
//Я - человек: Петров
сл.Инфо(); // Дополнительно определенный метод
//Я - служащий: Сидоров ______ Рога и копыта
вл.Инфо(); //Дополнительно определенный метод
//Я - владелец: Петров --> A777AA-99RUS
}
}
Для работы с объектом-потомком могут быть использованы ссылки типа Предок и Потомок. Ссылке базового класса можно присвоить ссылку на класс-потомок. Ссылки типа Предок можно использовать только для доступа к унаследованным полям, методам и свойствам (предок ничего не знает о том, что «привнесли» потомки).
|
class Человек
{
protected string фам;
public Человек(string фам)
{
this.фам = фам;
}
public void Показать()
{
Console.WriteLine("Я - человек: " + фам);
}
}
class Владелец: Человек
{
private string ном;
public Владелец(string фам, string ном): base(фам)
{
this.ном = ном;
}
public void Инфо()
{
Console.WriteLine("Я - владелец: "+фам+" --> " + ном);
}
}
class Program
{
static void Main(string[] args)
{
Человек ч;
Владелец вл = new Владелец("Петров", "A777AA-99RUS");
ч = вл;
ч.Показать(); //Унаследованный метод
//Я - человек: Петров
вл.Показать(); //Унаследованный метод
//Я - человек: Петров
ч.Инфо(); //Ошибка
вл.Инфо(); //Дополнительно определенный метод
//Я - владелец: Петров --> A777AA-99RUS
}
}
Возможность усложнять базовый класс может рассматриваться как альтернатива агрегации. При этом необходимо помнить, что механизмы наследования и агрегации реализуются по-разному.
Наследование – это аналог производства изделия «с нуля». Базовый чертеж (класс) дополняется и полученный чертеж изделия (класс-наследник) используется для изготовления изделия (объекта). На этапе выполнения нет деления объекта на предка и потомка – это единый объект.
Агрегация – аналог производства изделия методом «отверточной сборки». Изготовленные по отдельным чертежам детали (объекты) собираются в более сложное изделие (объект). Класс-контейнер – это сборочный чертеж изделия, определяющий его составные части. На этапе выполнения сохраняется четкое деление объекта на контейнер и часть. Это самостоятельные объекты.
class Человек
{
public string фам;
public Человек(string фам)
{
this.фам = фам;
}
public void Показать()
{
Console.WriteLine("Я - человек: " + фам);
}
}
class Владелец
{
public Человек чел;
private string ном;
public Владелец(Человек чел, string ном)
{
this.чел = чел;
this.ном = ном;
}
public void Инфо()
{
Console.WriteLine("Я - владелец: "+чел.фам+" --> " + ном);
}
}
class Program
{
static void Main(string[] args)
{
Человек ч = new Человек("Петров");
Владелец вл = new Владелец(ч,"A777AA-99RUS");
вл.чел.Показать();
//Я - человек: Петров
вл.Инфо();
//Я - владелец: Петров --> A777AA-99RUS
}
}
- Наследование как средство изменения базового класса
Реализуется через переопределение полей, методов и свойств базового класса в классе-потомке. На практике чаще всего используется изменение поведения – т.е. переопределение методов.
Синтаксически это реализуется как определение в классе-потомке метода с тем же именем, составом параметров и возвращаемым значением, что и в базовом классе.
С# поддерживает два способа переопределения метода, определенного в базовом классе на метод, определенный в классе-потомке (Рис.6):
1. new – новая версия метода. Решение о вызываемом методе принимается по типу ссылки. Через ссылку класса-предка вызывается метод, определенный в классе предке, а через ссылку класса-потомка вызывается метод, определенный в классе-потомке.
2. override – метод, заменяющий метод предка. Метод может быть вызван через ссылку класса-потомка или ссылку базового класса. Решение о вызываемом методе принимается по типу объекта, на который указывает ссылка. Для реализации этого способа метод в классе-предке должен быть объявлен как виртуальный (virtual).
class Человек
{
protected string фам;
public Человек(string фам)
{
this.фам = фам;
}
public virtual void Показать()
{
Console.WriteLine("Я - человек: " + фам);
}
}
class Студент:Человек
{
private string вуз;
public Студент(string фам, string вуз):base(фам)
{
this.вуз = вуз;
}
}
class Владелец: Человек
{
private string ном;
public Владелец(string фам, string ном): base(фам)
{
this.ном = ном;
}
public new void Показать()
{
Console.WriteLine("Я - владелец: " + фам + " --> " + ном);
}
}
class Служащий: Человек
{
private string фирма;
public Служащий(string фам, string фирма): base(фам)
{
this.фирма = фирма;
}
public override void Показать()
{
Console.WriteLine("Я - служащий: "+фам +" ____ "+ фирма);
}
}
class Program
{
static void Main(string[] args)
{
Человек ч;
Студент ст = new Студент("Иванов","ВШЭ");
Владелец вл = new Владелец("Петров", "A777AA-99RUS");
Служащий сл = new Служащий("Сидоров", "Рога и копыта");
ч = ст;
ч.Показать(); //Вызов метода предка
//Я - человек: Иванов
ст.Показать(); //Вызов метода предка
//Я - человек: Иванов
ч = вл;
ч.Показать(); //Вызов метода предка
//Я - человек: Петров
вл.Показать(); //Вызов метода наследника
//Я - владелец: Петров --> A777AA-99RUS
ч = сл;
ч.Показать(); //Вызов метода наследника
//Я - служащий: Сидоров ____ Рога и копыта
сл.Показать(); //Вызов метода наследника
//Я - служащий: Сидоров ____ Рога и копыта
}
}
Применение вызова переопределенного метода override позволяет просто решить проблему определения вызываемого метода при хранении в массиве объектов разного типа. Массив организуется как массив ссылок базового типа. Вызываемый метод определяется автоматически по типу объекта, на который указывает ссылка.
Метод в базовом классе может быть объявлен как абстрактный. Такой метод не содержит реализации – тела. Это только заголовок. Другими словами, базовый класс только декларирует общее поведение, а реализовать его обязаны потомки. В этом отличие абстрактных методов от виртуальных. Виртуальный метод может, но не обязан быть переопределен в потомках. Абстрактный метод должен быть переопределен в классе-потомке в обязательном порядке.
Класс, содержащий хотя бы один абстрактный метод, является абстрактным, о чем должно быть указано в определении класса
abstract class ИмяКласса
{
……………………………
}
Потомки обязаны переопределять абстрактный метод с модификатором override (new невозможно, т.к. у предка нет никакой версии реализации метода).
abstract class Человек
{
protected string фам;
public Человек(string фам)
{
this.фам = фам;
}
public abstract void Показать();
}
class Студент:Человек
{
private string вуз;
public Студент(string фам, string вуз):base(фам)
{
this.вуз = вуз;
}
public override void Показать()
{
Console.WriteLine("Я - студент: " + фам + " " + вуз);
}
}
class Владелец: Человек
{
private string ном;
public Владелец(string фам, string ном): base(фам)
{
this.ном = ном;
}
public override void Показать()
{
Console.WriteLine("Я - владелец: " + фам + " --> " + ном);
}
}
class Служащий: Человек
{
private string фирма;
public Служащий(string фам, string фирма): base(фам)
{
this.фирма = фирма;
}
public override void Показать()
{
Console.WriteLine("Я - служащий: "+фам + " ____ " + фирма);
}
}
class Program
{
static void Main(string[] args)
{
Человек ч;
Студент ст = new Студент("Иванов","ВШЭ");
Владелец вл = new Владелец("Петров", "A777AA-99RUS");
Служащий сл = new Служащий("Сидоров", "Рога и копыта");
ч = ст;
ч.Показать(); //Вызов метода наследника
//Я - студент: Иванов ВШЭ
ст.Показать(); //Вызов метода наследника
//Я - студент: Иванов ВШЭ
ч = вл;
ч.Показать(); //Вызов метода наследника
//Я - владелец: Петров --> A777AA-99RUS
вл.Показать(); //Вызов метода наследника
//Я - владелец: Петров --> A777AA-99RUS
ч = сл;
ч.Показать(); //Вызов метода наследника
//Я - служащий: Сидоров ____ Рога и копыта
сл.Показать(); //Вызов метода наследника
//Я - служащий: Сидоров ____ Рога и копыта
}
}
Объекты абстрактного класса создать невозможно.
Дата добавления: 2019-02-08; просмотров: 627;