Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Виртуальные методы в Си-шарп. Переопределение методов




В этом уроке мы рассмотрим с вами некоторые инструменты, с помощью которых в Си-шарп реализуется полиморфизм - виртуальные методы, переопределение методов.

Виртуальный метод – это метод, который может быть переопределен в классе наследнике.

Переопределение метода – это изменение его реализации в классе наследнике. Переопределив метод, он будет работать по-разному в базовом классе и классе наследнике, имея при этом одно и то же имя и аргументы и тип возврата.

Виртуальный метод объявляется при помощи ключевого слова virtual:

[модификатор доступа] virtual [тип] [имя метода] ([аргументы])
{
// тело метода
}


*Статический метод не может быть виртуальным.

Объявив виртуальный метод, мы теперь можем переопределить его в классе наследнике. Для этого используется ключевое слово override:

[модификатор доступа] override [тип] [имя метода] ([аргументы])
{
// новое тело метода
}


Рассмотрим это на примере. У нас есть класс Человек, и от него наследуются еще два – Студент и Ученик. В базовом классе есть виртуальный метод ShowInfo, который выводит информацию об объекте. В классах Студент и Ученик этот метод переопределяется для того, чтобы к выводу базовой информации добавить еще специфическую, относящеюся к соответствующему классу:

class Person
{
public string Name { get; set; }
public int Age { get; set; }

public Person(string name, int age)
{
Name = name;
Age = age;
}
public virtual void ShowInfo() //объявление виртуального метода
{
Console.WriteLine("Человек\nИмя: " + Name + "\n" + "Возраст: " + Age + "\n");
}
}
class Student: Person
{
public string HighSchoolName { get; set; }

public Student(string name, int age, string hsName)
: base(name, age)
{
HighSchoolName = hsName;
}
public override void ShowInfo() // переопределение метода
{
Console.WriteLine("Студент\nИмя: " + Name + "\n" + "Возраст: " + Age +"\n"+ "Название ВУЗа: " + HighSchoolName + "\n");
}
}
class Pupil: Person
{
public string Form { get; set; }

public Pupil(string name, int age, string form)
: base(name, age)
{
Form = form;
}
public override void ShowInfo() // переопределение метода
{
Console.WriteLine("Ученик(ца)\nИмя: " + Name + "\n" + "Возраст: " + Age + "\n" + "Класс: " + Form + "\n");
}
}

class Program
{

static void Main(string[] args)
{
List<Person> persons = new List<Person>();
persons.Add(new Person("Василий", 32));
persons.Add(new Student("Андрей", 21, "МГУ"));
persons.Add(new Pupil("Елена", 12, "7-Б"));

foreach (Person p in persons)
p.ShowInfo();

Console.ReadKey();
}
}


В методе main мы создаем список людей, в который добавляем просто человека, студента и ученика, и дальше выводим информацию о каждом из них вызовом метода ShowInfo(). Результат работы – вывод информации соответственно типу объекта.

А теперь может возникнуть вопрос – а что будет, если убрать переопределение, откинув ключевые слова virtual и override? В таком случае, в базовом классе и в классе наследнике будут методы с одинаковым именем ShowInfo. Программа работать будет, но о каждом объекте, независимо это просто человек или студент/ученик, будет выводиться информация только как о простом человеке (будет вызываться метод ShowInfo из базового класса).

Это можно исправить, добавив проверки на тип объекта, и при помощи приведения типов, вызывать нужный метод ShowInfo:

foreach (Person p in persons)
{
if (p is Student)
((Student)p).ShowInfo();
else if (p is Pupil)
((Pupil)p).ShowInfo();
else p.ShowInfo();
}


Только вот, сколько лишнего кода от этого появляется. Думаю, становится понятно, что нам дают виртуальные методы и переопределение.

Вызов базового метода

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

public virtual void ShowInfo() // ShowInfo в классе Person
{
Console.WriteLine("Имя: " + Name);
Console.WriteLine("Возраст: " + Age);
}

public override void ShowInfo() // ShowInfo в классе Student
{
base.ShowInfo(); // вызывает базовый метод ShowInfo()
Console.WriteLine("Название ВУЗа: " + HighSchoolName);
}

 

Домашнее задание

Создайте три класса: Воин, Воин_в_легких_доспехах и Воин_в_тяжелых_доспехах. У всех них есть свойство – Количество_жизней, а также метод Получить_урон, который принимает в качестве аргумент значение получаемого урона. Реализуйте этот метод по-разному для всех типов, установив различные коэффициенты в зависимости от типа доспехов в формуле вычета здоровья.

Абстрактные классы, методы и свойства в Си-шарп

В этом уроке мы продолжим с вами рассматривать инструменты полиморфизма. На очереди у нас абстрактные классы, методы и свойства.

Абстрактные классы

Абстрактный класс – это класс объявленный с ключевым словом abstract:

abstract class [имя_класса]
{
//тело
}


Такой класс имеет следующие особенности:
- нельзя создавать экземпляры (объекты) абстрактного класса;
- абстрактный класс может содержать как абстрактные методы/свойства, так и обычные;
- в классе наследнике должны быть реализованы все абстрактные методы и свойства, объявленные в базовом классе.

Зачем нужны абстрактные классы?

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

Абстрактные методы

Понимание абстрактных методов не будет для вас сложной задачей, если вы уже ознакомились с виртуальными методами.

Абстрактный метод – это метод, который не имеет своей реализации в базовом классе, и он ДОЛЖЕН быть реализован в классе-наследнике. Абстрактный метод может быть объявлен только в абстрактном классе.

Какая же разница между виртуальным и абстрактным методом?

- Виртуальный метод может иметь свою реализацию в базовом классе, абстрактный – нет (тело пустое);
- Абстрактный метод должен быть реализован в классе наследнике, виртуальный метод переопределять необязательно.

Объявление абстрактного метода происходит при помощи ключевого слова abstract, и при этом фигурные скобки упускаются, точка с запятой ставится после заголовка метода:

[модификатор доступа] abstract [тип] [имя метода] ([аргументы]);

 

Реализация абстрактного метода в классе наследнике происходит так же, как и переопределение метода – при помощи ключевого слова override:

[модификатор доступа] override [тип] [имя метода] ([аргументы])
{
// реализация метода
}


Абстрактные свойства

Создание абстрактных свойств не сильно отличается от методов:

[модификатор доступа] abstract [тип] [имя свойства] { get; set; }

 

Реализация в классе-наследнике:

[модификатор доступа] override [тип] [имя свойства]
{
get { тело аксессора get }
set { тело аксессора set }
}

 

Пример

В качестве примера, приведу программу похожую на ту, которая была в предыдущем уроке о виртуальных методах, где выводилась информация о человеке/студенте/школьнике. Сейчас уже будут животные. Тогда мы могли создать человека без статуса (не студент, не школьник), у которого была некоторая информация, а сейчас у нас будет абстрактная сущность Животное, объект которой создавать нельзя и нет смысла, так как каждое животное будет конкретного подцарства – млекопитающее, рыба, птица:

abstract class Animal
{
public string Name { get; set; }
public string Type { get; protected set; }

public abstract void GetInfo(); // объявление абстрактного метода
}
class Parrot: Animal
{
public Parrot(string name)
{
Name = name;
Type = "Птица";
}
public override void GetInfo() // реализация абстрактного метода
{
Console.WriteLine("Тип: " + Type + "\n" + "Имя: " + Name + "\n");
}
}
class Cat: Animal
{
public Cat(string name)
{
Name = name;
Type = "Млекопитающее";
}
public override void GetInfo() // реализация абстрактного метода
{
Console.WriteLine("Тип: " + Type + "\n" + "Имя: " + Name + "\n");
}
}
class Tuna: Animal
{
public Tuna(string name)
{
Name = name;
Type = "Рыба";
}
public override void GetInfo() // реализация абстрактного метода
{
Console.WriteLine("Тип: " + Type + "\n" + "Имя: " + Name+"\n");
}
}
class Program
{
static void Main(string[] args)
{
List<Animal> animals = new List<Animal>();
animals.Add(new Parrot("Кеша"));
animals.Add(new Cat("Пушок"));
animals.Add(new Tuna("Тёма"));

foreach (Animal animal in animals)
animal.GetInfo();

Console.ReadKey();
}
}


В итоге, мы все так же работает с одним списком животных, и, вызывая один метод GetInfo(), мы получаем информацию о соответствующем животном.

При попытке создать объект абстрактного класса мы получим ошибку "Cannot create an instance of the abstract class or interface 'ConsoleApplication1.Animal'":

Animal animal = new Animal(); // ошибка

 


Домашнее задание

Создайте абстрактный класс Человек, пусть там будет свойство Имя и абстрактный метод СказатьПриветствие(), от этого класса будет несколько наследников, которые представляют национальность (русский, украинец, американец...). Должно пулучиться так, что при вызове метода СказатьПриветствие() выводилось приветствие на языке соответствующему национальности (Привет, Привіт, Hi...).

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...