Поделиться через


abstract (Справочник по C#)

Модификатор abstract указывает, что изменяемый элемент имеет отсутствующую или неполную реализацию. Модификатор abstract можно использовать с классами, методами, свойствами, индексаторами и событиями. Используйте модификатор abstract в объявлении класса, чтобы указать, что класс предназначен только для использования в качестве базового класса для других классов и не должен быть создан сам по себе. Элементы с пометкой abstract должны быть реализованы не абстрактными классами, производными от абстрактного класса.

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

Пример 1. Абстрактный класс с смешанными элементами

В следующем примере демонстрируется абстрактный класс, содержащий как реализованные методы, так и абстрактные члены:

namespace LanguageKeywords;

public abstract class Vehicle
{
    protected string _brand;
    
    // Constructor - implemented method in abstract class
    public Vehicle(string brand) => _brand = brand;
    
    // Implemented method - provides functionality that all vehicles share
    public string GetInfo() => $"This is a {_brand} vehicle.";
    
    // Another implemented method
    public virtual void StartEngine() => Console.WriteLine($"{_brand} engine is starting...");
    
    // Abstract method - must be implemented by derived classes
    public abstract void Move();
    
    // Abstract property - must be implemented by derived classes  
    public abstract int MaxSpeed { get; }
}

public class Car : Vehicle
{
    public Car(string brand) : base(brand) { }
    
    // Implementation of abstract method
    public override void Move() => Console.WriteLine($"{_brand} car is driving on the road.");
    
    // Implementation of abstract property
    public override int MaxSpeed => 200;
}

public class Boat : Vehicle
{
    public Boat(string brand) : base(brand) { }
    
    // Implementation of abstract method
    public override void Move() => Console.WriteLine($"{_brand} boat is sailing on the water.");
    
    // Implementation of abstract property
    public override int MaxSpeed => 50;
}

public class AbstractExample
{
    public static void Examples()
    {
        // Cannot instantiate abstract class: Vehicle v = new Vehicle("Generic"); // Error!
        
        Car car = new Car("Toyota");
        Boat boat = new Boat("Yamaha");
        
        // Using implemented methods from abstract class
        Console.WriteLine(car.GetInfo());
        car.StartEngine();
        
        // Using abstract methods implemented in derived class
        car.Move();
        Console.WriteLine($"Max speed: {car.MaxSpeed} km/h");
        
        Console.WriteLine();
        
        Console.WriteLine(boat.GetInfo());
        boat.StartEngine();
        boat.Move();
        Console.WriteLine($"Max speed: {boat.MaxSpeed} km/h");
    }
}

class Program
{
    static void Main()
    {
        AbstractExample.Examples();
    }
}
/* Output:
This is a Toyota vehicle.
Toyota engine is starting...
Toyota car is driving on the road.
Max speed: 200 km/h

This is a Yamaha vehicle.
Yamaha engine is starting...
Yamaha boat is sailing on the water.
Max speed: 50 km/h
*/

В этом примере абстрактный Vehicle класс предоставляет:

  • Реализованные элементы: GetInfo() метод, StartEngine() метод и конструктор — они предоставляют общие функциональные возможности для всех транспортных средств.
  • Абстрактные члены: Move() метод и MaxSpeed свойство — они должны быть реализованы по каждому конкретному типу транспортного средства.

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

Пример 2

В этом примере класс Square должен обеспечивать реализацию GetArea, поскольку является производным от класса Shape:

abstract class Shape
{
    public abstract int GetArea();
}

class Square : Shape
{
    private int _side;

    public Square(int n) => _side = n;

    // GetArea method is required to avoid a compile-time error.
    public override int GetArea() => _side * _side;

    static void Main()
    {
        var sq = new Square(12);
        Console.WriteLine($"Area of the square = {sq.GetArea()}");
    }
}
// Output: Area of the square = 144

Абстрактные классы предоставляют следующие возможности:

  • Создавать экземпляры абстрактного класса нельзя.

  • Абстрактный класс может содержать абстрактные методы и методы доступа.

  • Абстрактный класс также может содержать реализованные методы, свойства, поля и другие члены, предоставляющие функциональные возможности производным классам.

  • Изменить абстрактный класс с модификатором sealed нельзя, так как два этих модификатора имеют взаимоисключающие значения. Модификатор sealed запрещает наследование класса, в то время как модификатор abstract указывает, что класс обязан иметь производные классы.

  • Неабстрактный класс, производный от абстрактного класса, должен включать фактические реализации всех наследуемых абстрактных методов и методов доступа.

Модификатор abstract в объявлении метода или свойства позволяет указать, что этот метод или свойство не содержат реализации.

Абстрактные методы предоставляют следующие возможности:

  • Абстрактный метод неявно представляет собой виртуальный метод.

  • Объявления абстрактных методов допускаются только в абстрактных классах.

  • Поскольку объявление абстрактного метода не предоставляет фактической реализации, тело метода отсутствует, а объявление метода заканчивается точкой с запятой, и фигурных скобок ({ }) после подписи нет. Например:

    public abstract void MyMethod();  
    

    Реализация предоставляется методом override, который является членом неабстрактного класса.

  • В объявлении абстрактного метода нельзя использовать статические или виртуальные модификаторы.

Действие абстрактных свойств аналогично абстрактным методам, за исключением отличий в синтаксисе объявлений и вызовов.

  • Использование модификатора abstract в статическом свойстве является недопустимым.

  • Абстрактное наследуемое свойство можно переопределить в производном классе, включив объявление свойства, которое использует модификатор override.

Дополнительные сведения об абстрактных классах см. в статье Abstract and Sealed Classes and Class Members (Абстрактные и запечатанные классы и члены классов).

Абстрактный класс должен предоставлять реализацию для всех членов интерфейса.

Абстрактный класс, реализующий интерфейс, может сопоставлять методы интерфейса с абстрактными методами. Например:

interface I
{
    void M();
}

abstract class C : I
{
    public abstract void M();
}

Пример 3

В следующем примере класс DerivedClass является производным от абстрактного класса BaseClass. Абстрактный класс содержит абстрактный метод, AbstractMethod, и два абстрактных свойства, X и Y.

// Abstract class
abstract class BaseClass
{
    protected int _x = 100;
    protected int _y = 150;

    // Abstract method
    public abstract void AbstractMethod();

    // Abstract properties
    public abstract int X { get; }
    public abstract int Y { get; }
}

class DerivedClass : BaseClass
{
    public override void AbstractMethod()
    {
        _x++;
        _y++;
    }

    public override int X   // overriding property
    {
        get
        {
            return _x + 10;
        }
    }

    public override int Y   // overriding property
    {
        get
        {
            return _y + 10;
        }
    }

    static void Main()
    {
        var o = new DerivedClass();
        o.AbstractMethod();
        Console.WriteLine($"x = {o.X}, y = {o.Y}");
    }
}
// Output: x = 111, y = 161

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

BaseClass bc = new BaseClass();   // Error  

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

Тем не менее можно использовать конструктор абстрактного класса, как показано в примере ниже.

Пример 4

public abstract class Shape
{
    public string Color { get; set; }

    // Constructor of the abstract class
    protected Shape(string color)
    {
        Color = color;
        Console.WriteLine($"Created a shape with color {color}.");
    }

    // Abstract method that must be implemented by derived classes
    public abstract double CalculateArea();
}

public class Square : Shape
{
    public double Side { get; set; }

    // Constructor of the derived class calling the base class constructor
    public Square(string color, double side) : base(color)
    {
        Side = side;
    }

    public override double CalculateArea()
    {
        return Side * Side;
    }
}

public class Program
{
    public static void Main(string[] args)
     {
            Square square = new Square("red", 5);
            Console.WriteLine($"Area of the square: {square.CalculateArea()}");            
     }
}

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

  • Несмотря на то, что объекты абстрактного класса невозможно создать, он по-прежнему может иметь конструктор. Этот конструктор обычно protected, то есть доступ к нему можно получить только из производных классов. В этом случае конструктор Shape принимает параметр color и инициализирует свойство Color. Он также выводит сообщение в консоль. Часть public Square(string color, double side) : base(color) вызывает конструктор базового класса (Shape) и передает в него аргумент color.
  • В классе Shape определенный конструктор принимает цвет в качестве параметра protected Shape(string color). Это означает, что C# больше не предоставляет конструктор без параметров по умолчанию, поэтому производные классы должны использовать выражение : base(color) для вызова базового конструктора. Установка значения по умолчанию для цвета protected Shape(string color="green") позволит опустить выражение : base(color) в производных классах, при этом будет вызван конструктор protected Shape(string color="green"), устанавливающий цвет зелёный.

Спецификация языка C#

Дополнительные сведения см. в спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.

См. также