Compartir a través de


Clases abstractas (F#)

Las clases abstractas son clases que dejan algunos o todos los miembros sin implementar para que las clases derivadas puedan proporcionar las implementaciones.

// 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

Comentarios

En la programación orientada a objetos, se usa una clase abstracta como clase base de una jerarquía y dicha clase abstracta representa la funcionalidad común de un conjunto diverso de tipos de objeto. Tal y como indica el término "abstracto", en muchas ocasiones las clases abstractas no se corresponden directamente con entidades concretas del dominio del problema. Sin embargo, representan lo que muchas entidades concretas diferentes tienen en común.

Las clases abstractas deben tener el atributo AbstractClass. Pueden tener miembros implementados y sin implementar. Cuando se aplica a una clase, el término abstracto tiene el mismo significado que en otros lenguajes .NET. Sin embargo, cuando se aplica a métodos (y propiedades), el término abstracto tiene un significado un tanto diferente en F# con respecto a otros lenguajes .NET. En F#, cuando un método está marcado con la palabra clave abstract, significa que un miembro tiene una entrada, conocida como entrada de distribución virtual en la tabla interna de funciones virtuales de ese tipo. Es decir, el método es virtual aunque no se use la palabra clave virtual en el lenguaje F#. La palabra clave abstract se utiliza con métodos virtuales, independientemente de que estén implementados o no. La declaración de una entrada de distribución virtual es independiente de la definición de un método para dicha entrada de distribución. Por consiguiente, el equivalente en F# de una definición y una declaración de método virtual en otro lenguaje .NET es la combinación de una declaración de método abstracto y una definición independiente con la palabra clave default u override. Para obtener más información y ejemplos, vea Métodos (F#).

Una clase se considera abstracta únicamente si hay métodos abstractos declarados pero sin definir. Por consiguiente, las clases que tienen métodos abstractos no son necesariamente clases abstractas. A menos que una clase tenga métodos abstractos sin definir, no utilice el atributo AbstractClass.

En la sintaxis anterior, accessibility-modifier puede ser public, private o internal. Para obtener más información, vea Control de acceso (F#).

Tal y como sucede con otros tipos, las clases abstractas pueden tener una clase base y una o varias interfaces base. Cada clase o interfaz base aparece en una línea independiente junto con la palabra clave inherit.

La definición de tipo de una clase abstracta puede contener miembros totalmente definidos pero también miembros abstractos. En la sintaxis anterior, se muestra por separado la sintaxis de los miembros abstractos. En esta sintaxis, la type signature de un miembro es una lista que contiene los tipos de los parámetros por orden y los tipos de valores devueltos, separados por los tokens -> tokens and/or *, según corresponda para los parámetros currificados y los parámetros de tupla. La sintaxis de las signaturas de tipo de los miembros abstractos es idéntica a la que se usa en los archivos de signatura y la que muestra IntelliSense en el Editor de código de Visual Studio.

Ejemplo

En el código siguiente, se muestra la clase abstracta Shape que tiene dos clases derivadas no abstractas, Square y Circle. En el ejemplo se muestra cómo se utilizan las clases abstractas, los métodos y las propiedades. En el ejemplo, la clase abstracta Shape representa los elementos comunes de las entidades concretas Circle y Square. Las características comunes de todas las formas (en un sistema de coordenadas bidimensional) se abstraen en la clase Shape: la posición en la cuadrícula, un ángulo de rotación y las propiedades de área y perímetro. Estas características se pueden reemplazar, salvo la posición, cuyo comportamiento no lo pueden cambiar las formas individuales.

Se puede reemplazar el método de rotación, como en la clase Circle, que es invariante con respecto a la rotación dada su simetría. Por lo tanto, en la clase Circle, el método de rotación se reemplaza por un método que no hace nada.

// 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
  

Vea también

Referencia

Clases (F#)

Métodos (F#)

Propiedades (F#)

Otros recursos

Miembros (F#)