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

Абстрактные классы — это классы , которые покидают некоторые или все члены, чтобы реализации могли предоставляться производными классами.

Синтаксис

// Abstract class syntax.
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
[ inherit base-class-or-interface-name ]
[ abstract-member-declarations-and-member-definitions ]

// Abstract member syntax.
abstract member member-name : type-signature

Замечания

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

Абстрактные классы должны иметь AbstractClass атрибут. Они могут реализовывать и неуправляемые члены. Использование термина абстрактным при применении к классу совпадает с другими языками .NET. Однако использование термина абстрактным при применении к методам (и свойствам) немного отличается от использования в F# на других языках .NET. В F#, если метод помечается ключевое слово abstract , это означает, что элемент имеет запись, известную как виртуальный слот диспетчера, во внутренней таблице виртуальных функций для этого типа. Другими словами, метод является виртуальным, хотя virtual ключевое слово не используется в F#. Ключевое слово abstract используется для виртуальных методов независимо от того, реализуется ли этот метод. Объявление виртуального слота диспетчера отличается от определения метода для этого слота отправки. Таким образом, эквивалент F# объявления и определения виртуальных методов в другом языке .NET является сочетанием объявления абстрактного метода и отдельного определения с default ключевое слово или override ключевое слово. Дополнительные сведения и примеры см. в разделе "Методы".

Класс считается абстрактным, только если существуют абстрактные методы, объявленные, но не определенные. Поэтому классы, имеющие абстрактные методы, не обязательно являются абстрактными классами. Если класс не имеет неопределенных абстрактных методов, не используйте атрибут AbstractClass .

В предыдущем синтаксисе можно использовать publicprivate модификатор специальных возможностей или internal. Дополнительные сведения см. в разделе Access Control.

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

Определение типа абстрактного класса может содержать полностью определенные элементы, но также может содержать абстрактные элементы. Синтаксис абстрактных элементов показан отдельно в предыдущем синтаксисе. В этом синтаксисе сигнатура типа элемента — это список, содержащий типы параметров в порядке и возвращаемые типы, разделенные -> маркерами и (или) * маркерами, соответствующими курируемым и кортежным параметрам. Синтаксис подписей абстрактного типа члена совпадает с тем, что используется в файлах подписей и показан intelliSense в редакторе Visual Studio Code.

Следующий код иллюстрирует абстрактный класс Shape, имеющий два не абстрактных производных класса, квадрат и круг. В примере показано, как использовать абстрактные классы, методы и свойства. В примере фигура абстрактного класса представляет общие элементы конкретного круга сущностей и квадрата. Общие признаки всех фигур (в двухмерной системе координат) абстрагируются в класс Фигуры: положение в сетке, угол поворота и свойства области и периметра. Их можно переопределить, за исключением положения, поведение которых отдельные фигуры не могут измениться.

Метод поворота можно переопределить, как в классе Circle, который является инвариантным поворотом из-за его симметрии. Поэтому в классе Circle метод поворота заменяется методом, который ничего не делает.

// An abstract class that has some methods and properties defined
// and some left abstract.
[<AbstractClass>]
type Shape2D(x0: float, y0: float) =
    let mutable x, y = x0, y0
    let mutable rotAngle = 0.0

    // These properties are not declared abstract. They
    // cannot be overriden.
    member this.CenterX
        with get () = x
        and set xval = x <- xval

    member this.CenterY
        with get () = y
        and set yval = y <- yval

    // These properties are abstract, and no default implementation
    // is provided. Non-abstract derived classes must implement these.
    abstract Area: float with get
    abstract Perimeter: float with get
    abstract Name: string with get

    // This method is not declared abstract. It cannot be
    // overridden.
    member this.Move dx dy =
        x <- x + dx
        y <- y + dy

    // An abstract method that is given a default implementation
    // is equivalent to a virtual method in other .NET languages.
    // Rotate changes the internal angle of rotation of the square.
    // Angle is assumed to be in degrees.
    abstract member Rotate: float -> unit
    default this.Rotate(angle) = rotAngle <- rotAngle + angle

type Square(x, y, sideLengthIn) =
    inherit Shape2D(x, y)
    member this.SideLength = sideLengthIn
    override this.Area = this.SideLength * this.SideLength
    override this.Perimeter = this.SideLength * 4.
    override this.Name = "Square"

type Circle(x, y, radius) =
    inherit Shape2D(x, y)
    let PI = 3.141592654
    member this.Radius = radius
    override this.Area = PI * this.Radius * this.Radius
    override this.Perimeter = 2. * PI * this.Radius
    // Rotating a circle does nothing, so use the wildcard
    // character to discard the unused argument and
    // evaluate to unit.
    override this.Rotate(_) = ()
    override this.Name = "Circle"

let square1 = new Square(0.0, 0.0, 10.0)
let circle1 = new Circle(0.0, 0.0, 5.0)
circle1.CenterX <- 1.0
circle1.CenterY <- -2.0
square1.Move -1.0 2.0
square1.Rotate 45.0
circle1.Rotate 45.0
printfn "Perimeter of square with side length %f is %f, %f" (square1.SideLength) (square1.Area) (square1.Perimeter)
printfn "Circumference of circle with radius %f is %f, %f" (circle1.Radius) (circle1.Area) (circle1.Perimeter)

let shapeList: list<Shape2D> = [ (square1 :> Shape2D); (circle1 :> Shape2D) ]
List.iter (fun (elem: Shape2D) -> printfn "Area of %s: %f" (elem.Name) (elem.Area)) shapeList

Выходные данные:

Perimeter of square with side length 10.000000 is 40.000000
Circumference of circle with radius 5.000000 is 31.415927
Area of Square: 100.000000
Area of Circle: 78.539816

См. также