Определяя элемент как абстрактный, вы определяете вид заполнителя, не предоставляя реализацию по умолчанию. Любой клиентский код, взаимодействующий с базовым классом, может по-прежнему ссылаться на абстрактный член-заполнитель, будучи уверенным в том, что конкретный класс экземпляра должен обеспечивать конкретную реализацию.
Определяя член как виртуальный, вы разрешаете производным классам предоставлять реализацию, которая будет переопределять реализацию базового класса. Разница в том, что если производный класс не предоставляет свою собственную реализацию, будет использоваться класс из базового класса.
Рассмотрим следующие примеры классов C #:
abstract class TaskBase {
public abstract void RunTask();
}
class RoadNetwork {
public string GetCorrectSideToDriveOn() { return "left"; }
}
Конструкция класса TaskBase
заставляет производный класс предоставлять собственную реализацию RunTask()
, потому что код не будет компилироваться без него. Дизайнер фактически говорит: «Задачи должны быть работоспособными, но вы должны предоставить реализацию, потому что не существует значимого значения по умолчанию».
Конструкция класса RoadNetwork
работает по-другому: любой, кто реализует RoadNetwork
, будет автоматически использовать правильную сторону дороги для движения, если только он специально не выберет для движения не ту сторону;)