Абстрактные классы (F#)
Абстрактные классы — это классы, которые оставляют некоторые или все члены нереализованными, реализации которых могут предоставляться производными классами.
// 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.Дополнительные сведения и примеры см. в разделе Методы (F#).
Класс считается абстрактным, только если имеются абстрактные методы, которые объявлены, но не определены.Следовательно, классы, имеющие абстрактные методы, не обязательно являются абстрактными классами.Используйте атрибут AbstractClass, только если класс имеет неопределенные абстрактные методы.
В предыдущей синтаксической конструкции accessibility-modifier может быть public, private или internal.Дополнительные сведения см. в разделе Управление доступом (F#).
Как и с другими типами, абстрактные классы могут иметь базовый класс и один или несколько базовых интерфейсов.Каждый базовый класс или интерфейс отображается на отдельной строке вместе с ключевым словом inherit.
Определение типа абстрактного класса может содержать как полностью определенные, так и абстрактные члены.Синтаксические структуры, обозначающие абстрактные члены, показаны в предыдущей синтаксической конструкции отдельно.В этой синтаксической конструкции параметр type signature члена представляет собой последовательный список типов параметров и типов возвращаемых значений, разделенных токенами -> и (или) * (в зависимости от того, являются параметры каррированными или кортежными).Синтаксис сигнатур типов абстрактных членов совпадает с синтаксисом файлов сигнатуры и синтаксисом, отображаемым с помощью IntelliSense в редакторе кода Visual Studio.
Пример
В следующем примере кода демонстрируется абстрактный класс Shape, имеющий два неабстрактных производных класса: Square и Circle.В следующем примере демонстрируется использование абстрактных классов, методов и свойств.В этом примере абстрактный класс Shape представляет общие элементы конкретных объектов (круга и квадрата).Общие признаки всех фигур (в двухмерной системе координат) абстрактно представлены с помощью класса 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
// overriden.
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