Classi astratte (F#)
Le classi astratte sono classi in cui tutti i membri, o alcuni di essi, non sono implementati, in modo che le implementazioni possano essere fornite dalle classi derivate.
// 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
Note
Nella programmazione orientata a oggetti una classe astratta viene utilizzata come classe di base di una gerarchia e rappresenta funzionalità comuni di un set di tipi di oggetti.Come indicato dal termine "astratto", le classi astratte spesso non corrispondono direttamente a entità concrete nel dominio del problema.Esse rappresentano tuttavia ciò che numerose entità concrete diverse hanno in comune.
Le classi astratte devono disporre dell'attributo AbstractClass.Esse possono includere membri implementati e non implementati.L'utilizzo del termine astratto applicato a una classe ha lo stesso significato che ha negli altri linguaggi .NET, mentre l'utilizzo del termine astratto applicato a metodi (e proprietà) è leggermente diverso in F# rispetto all'utilizzo negli altri linguaggi .NET.In F# quando un metodo è contrassegnato con la parola chiave abstract significa che un membro dispone di una voce, nota come slot di invio virtuale, nella tabella interna di funzioni virtuali per tale tipo.In altre parole, il metodo è virtuale, anche se la parola chiave virtual non viene utilizzata nel linguaggio F#.La parola chiave abstract viene utilizzata nei metodi virtuali indipendentemente dal fatto che il metodo sia implementato.La dichiarazione di uno slot di invio virtuale è distinta dalla definizione di un metodo per tale slot di invio.L'equivalente F# di una dichiarazione e di una definizione di metodo virtuale in un altro linguaggio .NET è pertanto una combinazione di una dichiarazione di metodo astratta e una definizione distinta, con la parola chiave default o la parola chiave override.Per ulteriori informazioni ed esempi, vedere Metodi (F#).
Una classe è considerata astratta solo se vi sono metodi astratti dichiarati ma non definiti.Le classi che includono metodi astratti non sono pertanto necessariamente classi astratte.A meno che una classe non disponga di metodi astratti non definiti, non utilizzare l'attributo AbstractClass.
Nella sintassi precedente il valore di accessibility-modifier può essere public, private o internal.Per ulteriori informazioni, vedere Controllo di accesso (F#).
Come per altri tipi, le classi astratte possono disporre di una classe di base e di una o più interfacce di base.Ogni classe o interfaccia di base si trova su una riga separata insieme alla parola chiave inherit.
La definizione di tipo di una classe astratta può contenere membri completamente definiti, ma può contenere anche membri astratti.La sintassi per i membri astratti è illustrata separatamente nella sintassi precedente.In questa sintassi, l'elemento type signature di un membro è un elenco che contiene i tipi di parametro in ordine e i tipi restituiti, separati da token -> e/o *, in modo appropriato ai parametri sottoposti a currying o in formato di tupla.La sintassi per le firme del tipo dei membri astratti corrisponde a quella utilizzata nei file di firma e visualizzata da IntelliSense nell'editor di codice di Visual Studio.
Esempio
Nel codice seguente viene illustrata una classe astratta Shape che dispone di due classi derivate non astratte, Square e Circle.Nell'esempio viene illustrato come utilizzare classi, proprietà e metodi astratti.Nell'esempio la classe astratta Shape rappresenta gli elementi comuni delle entità concrete circle e square.Le funzionalità comuni di tutte le forme (in un sistema di coordinate bidimensionale) sono estratte nella classe Shape: la posizione nella griglia, un angolo di rotazione e le proprietà di area e perimetro.È possibile eseguire l'override di questi elementi, ad eccezione della posizione, il cui comportamento non può essere modificato per le singole forme.
È possibile eseguire l'override del metodo di rotazione, come nella classe Circle, che è invariante rispetto alla rotazione a causa della sua simmetria.Nella classe Circle, pertanto, il metodo di rotazione è sostituito da un metodo che non esegue alcuna operazione.
// 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