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

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

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

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

Виртуальный метод объявляется при помощи ключевого слова 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);
}

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

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

PS. Не забудьте поделиться с друзьями этой статьей с помощью кнопок социальных сетей.

Наши рекомендации