Наследование как средство усложнения базового класса


Реализуется путем определения в классе-потомке дополнительных полей, методов и свойств. Класс-потомок будет иметь:

• Унаследованные поля, методы и свойства

• Дополнительные поля, методы и свойства

Конструкторы не наследуются, поэтому класс-потомок должен иметь собственный конструктор. При создании объекта класса-потомка конструкторы должны вызываться, начиная с базового класса. В начале инициализируются унаследованные поля, потом – специфические поля, как показано на Рис.2.

Указанная очередность вызовов реализуется одним из способов:

1. Конструктор потомка не имеет вызова конструктора-предка. Автоматически вызывается конструктор предка без параметров.

2. Конструктор потомка явно вызывает конструктор предка.

Рис.3

 

Пример: определить базовый класс «Человек». Человек характеризуется фамилией и умением сообщать свою фамилию. Потомками класса «Человек» являются:

-класс «Владелец», дополнительно характеризующийся номером автомобиля и умением сообщать его

-класс “Служащий”, дополнительно характеризующийся названием фирмы и умением сообщать его.

-класс “Студент”, дополнительно характеризующийся названием ВУЗа

 

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

}

}

Для работы с объектом-потомком могут быть использованы ссылки типа Предок и Потомок. Ссылке базового класса можно присвоить ссылку на класс-потомок. Ссылки типа Предок можно использовать только для доступа к унаследованным полям, методам и свойствам (предок ничего не знает о том, что «привнесли» потомки).

 

Рис.4


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

}

}

  1. Наследование как средство изменения базового класса

Реализуется через переопределение полей, методов и свойств базового класса в классе-потомке. На практике чаще всего используется изменение поведения – т.е. переопределение методов.

Синтаксически это реализуется как определение в классе-потомке метода с тем же именем, составом параметров и возвращаемым значением, что и в базовом классе.

 

С# поддерживает два способа переопределения метода, определенного в базовом классе на метод, определенный в классе-потомке (Рис.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;


Поиск по сайту:

Воспользовавшись поиском можно найти нужную информацию на сайте.

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

Считаете данную информацию полезной, тогда расскажите друзьям в соц. сетях.
Poznayka.org - Познайка.Орг - 2016-2024 год. Материал предоставляется для ознакомительных и учебных целей.
Генерация страницы за: 0.041 сек.