Введение в ООП с примерами на C#. Часть четвёртая. Абстрактные классы
Комментарии
В закладки
27087
Рассказывает Akhil Mittal
На данный момент этот блок не поддерживается, но мы не забыли о нём!Наша команда уже занята его разработкой, он будет доступен в ближайшее время.
В прошлых статьях серии “Введение в ООП” мы рассматривали полиморфизм (а также его нюансы на практике) и наследование. В этой мы поговорим о самой захватывающей части ООП-парадигмы — об абстрактных классах. В целом концепция абстрактных классов в C# ничем не отличается от таковой в других языках, но в C# работать с ней приходится несколько иначе.
using System;
namespace InheritanceAndPolymorphism
{
public abstract class ClassA
{
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA classA = new ClassA();
Console.ReadKey();
}
}
}
Попытаемся скомпилировать этот код:
Что нужно запомнить: Мы не можем создать экземпляр абстрактного класса с помощью ключевого слова new.
Описание методов в абстрактном классе
Попробуем добавить в наш абстрактный класс немного кода:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA classA = new ClassA();
Console.ReadKey();
}
}
С кодом класса никаких проблем нет, но скомпилировать снова не получается, потому что нельзя создавать экземпляры абстрактных классов.
Использование абстрактного класса в качестве базового
Давайте попробуем создать ещё один класс:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA
/// </summary>
public class ClassB:ClassA
{
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
Вау. Теперь всё спокойно компилируется.
Что нужно запомнить: Мы можем унаследовать обычный класс от абстрактного.
Что нужно запомнить: Мы можем создать экземпляр обычного класса, унаследованного от абстрактного, с помощью ключевого слова new.
Декларация методов в абстрактном классе
Теперь попробуем сделать вот так:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
И… мы получаем ошибку компиляции:
Дело в том, что если мы объявляем метод в абстрактном классе и при этом хотим, чтобы его конкретное поведение было определено в производных классах, то к такому методу мы должны так же добавить ключевое слово abstract. Добавим его:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
abstract public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
…и снова получим ошибку компиляции:
Что нужно запомнить: Если мы хотим объявить метод в абстрактном классе, но не реализовывать его, к методу нужно добавить ключевое слово abstract.
Что нужно запомнить: Если мы объявляем абстрактный метод в абстрактном классе, то этот метод должен реализовываться в неабстрактных наследниках этого класса.
Реализация абстрактного метода в производном классе
Так, давайте тогда попробуем реализовать метод YYY() в классе ClassB:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
abstract public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
public void YYY()
{
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
На первый взгляд всё отлично, правда? Но на этот раз мы получим сразу две ошибки компиляции:
Дело в том, что в C# нужно явно объявить, что мы реализуем абстрактный метод класса-родителя с помощью ключевого слова override:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
abstract public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
public override void YYY()
{
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
Ура! ^_^ У нас наконец-то нет никаких ошибок!
Абстрактный метод базового класса и метод с override класса-наследника должны быть одинаковы
Это значит, что мы не можем менять тип возвращаемого значения или аргументы, которые передаются в метод. Например, если мы напишем такое:
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
abstract public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
public override int YYY()
{
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
То в консоли увидим следующую ошибку:
Инициализация переменных в абстрактных классах
В примерах выше, переменная int a будет обладать значением по умолчанию (0). Мы можем изменить его на нужное нам прямо в абстрактном классе — с этим не связано никаких особенностей.
Абстрактные методы в неабстрактных классах
/// <summary>
/// Abstract class ClassA
/// </summary>
public class ClassA
{
public int a;
public void XXX()
{
}
abstract public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
public override void YYY()
{
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
Такой код не скомпилируется:
Что нужно запомнить: Абстрактные методы могут быть объявлены только в абстрактных классах.
Вызов абстрактного метода родителя
/// <summary>
/// Abstract class ClassA
/// </summary>
public abstract class ClassA
{
public int a;
public void XXX()
{
}
abstract public void YYY();
}
/// <summary>
/// Derived class.
/// Class derived from abstract class ClassA.
/// </summary>
public class ClassB:ClassA
{
public override void YYY()
{
base.YYY();
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassB classB = new ClassB();
Console.ReadKey();
}
}
Вывод:
Разумеется, мы не можем исполнить код, которого не существует.
Абстрактный класс, который наследуется от другого абстрактного класса
/// <summary>
/// Base class ClassA
/// </summary>
public class ClassA
{
public virtual void XXX()
{
Console.WriteLine("ClassA XXX");
}
}
/// <summary>
/// Derived abstract class.
/// Class derived from base class ClassA.
/// </summary>
public abstract class ClassB:ClassA
{
public new abstract void XXX();
}
public class ClassC:ClassB
{
public override void XXX()
{
System.Console.WriteLine("ClassC XXX");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
ClassA classA = new ClassC();
ClassB classB = new ClassC();
classA.XXX(); classB.XXX();
}
}
Вывод:
Почему вывод именно такой, вы должны понимать из материалов третьей статьи этой серии.
Может ли абстрактный класс быть sealed
Проверим:
/// <summary>
/// sealed abstract class ClassA
/// </summary>
public sealed abstract class ClassA
{
public abstract void XXX()
{
Console.WriteLine("ClassA XXX");
}
}
/// <summary>
/// Program: used to execute the method.
/// Contains Main method.
/// </summary>
public class Program
{
private static void Main(string[] args)
{
}
}
И получим ошибку:
Разумеется, абстрактный класс не может быть sealed, т.к. он для того и создан, чтобы от него создавались производные классы.
Что нужно запомнить: Абстрактный класс не может иметь модификатор sealed.
Что нужно запомнить: Абстрактный класс не может иметь модификатор static.
Что мы узнали сегодня:
Что нужно запомнить
Мы не можем создать экземпляр абстрактного класса с помощью ключевого слова new;
Мы можем унаследовать обычный класс от абстрактного;
Мы можем создать экземпляр обычного класса, унаследованного от абстрактного, с помощью ключевого слова new;
Если мы хотим объявить метод в абстрактном классе, но не реализовывать его, к методу нужно добавить ключевое слово abstract;
Если мы объявляем абстрактный метод в абстрактном классе, то этот метод должен реализовываться в неабстрактных наследниках этого класса;
Абстрактные методы могут быть объявлены только в абстрактных классах;
Абстрактный класс не может иметь модификатор sealed;
Абстрактный класс не может иметь модификатор static.