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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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


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

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

protected [тип] [поле, которым управляет свойство];
[модификатор доступа] 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...).

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

ерегрузка методов в Си-шарп В этом уроке мы рассмотрим с вами еще один способ реализации полиморфизма в Си-шарп – перегрузку методов.

Перегрузка методов – это объявление в классе методов с одинаковыми именами при этом с различными параметрами.

Имея некий метод, чтобы его перегрузить, другой метод с таким же именем должен отличаться от него количеством параметров и/или типами параметров. Отличия только типами возвращаемых значений методами недостаточно для перегрузки, но если методы отличаются параметрами, тогда перегружаемые методы могут иметь и различные типы возвращаемых значений.

Пример того, как может быть перегружен метод:

public void SomeMethod()
{
// тело метода
}
public void SomeMethod(int a) // от первого отличается наличием параметра
{
// тело метода
}
public void SomeMethod(string s) // от второго отличается типом параметра
{
// тело метода
}
public int SomeMethod(int a, int b) // от предыдущих отличается количеством параметров (плюс изменен тип возврата)
{
// тело метода
return 0;
}


Пример того, как не может быть перегружен метод:

public void SomeMethod(int a)
{
// тело метода
}
public void SomeMethod(int b) // имени параметра недостаточно
{
// тело метода
}
public int SomeMethod(int a) // типа возвращаемого значения недостаточно
{
// тело метода
return 0;
}

Мы с вами уже сталкивались с перегрузкой методов на практике в уроке Конструкторы в Си-шарп, когда создавали несколько конструкторов - без параметров и с параметрами.

На практике перегрузка методов в Си-шарп встречается на каждом шагу. Например, в классе для конвертации типов Convert перегружены почти все методы. Метод ToInt32() может принимать параметр различного типа – bool, float, double, byte, char и т.д. Для каждого варианта параметра метода необходима своя реализация конвертации соответственно, здесь и используется перегрузка метода ToInt32(). Этот пример очень хорошо показывает преимущество, которое дает нам перегрузка – мы просто вызываем метод с универсальным именем ToInt32(), не задумываясь параметр какого типа мы передаем. Без перегрузки этот метод мог бы разделиться на методы с разными именами: FromBoolToInt32(), FromFloatToInt32(), FromDoubleToInt32(), FromByteToInt32() и т.д.

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

public static void AddAndDisplay(int a, int b)
{
Console.WriteLine(a + b);
}

public static void AddAndDisplay(char a, char b)
{
Console.WriteLine(a.ToString() + b.ToString());
}
static void Main(string[] args)
{
AddAndDisplay(5, 8); // 13
AddAndDisplay('C', '#'); // "C#"
Console.ReadKey();
}

Мы просто задаем параметры метода, а компилятор сам решает, какой вариант метода AddAndDisplay необходимо вызвать.

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

Имеется список имен. Создайте метод, который будет выводить на экран эти имена через запятую. Перегрузите этот метод так, чтобы можно было изменять разделитель – вместо запятых между именами любой символ, переданный параметром.

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

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