Métodos

Un método es una función asociada a un tipo. En la programación orientada a objetos, los métodos se usan para exponer e implementar la funcionalidad y el comportamiento de objetos y tipos.

Sintaxis

// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Static method definition.
[ attributes ]
static member [inline] method-name parameter-list [ : return-type ] =
    method-body

// Abstract method declaration or virtual dispatch slot.
[ attributes ]
abstract member method-name : type-signature

// Virtual method declaration and default implementation.
[ attributes ]
abstract member method-name : type-signature
[ attributes ]
default self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Override of inherited virtual method.
[ attributes ]
override self-identifier.method-name parameter-list [ : return-type ] =
    method-body

// Optional and DefaultParameterValue attributes on input parameters
[ attributes ]
[ modifier ] member [inline] self-identifier.method-name ([<Optional; DefaultParameterValue( default-value )>] input) [ : return-type ]

Comentarios

En la sintaxis anterior, puede ver las distintas formas de declaraciones y definiciones de método. En cuerpos de método más largos, aparece un salto de línea después del signo igual (=) y se aplica sangría a todo el cuerpo del método.

Se pueden aplicar atributos a cualquier declaración de método. Preceden a la sintaxis de una definición de método y normalmente se enumeran en una línea independiente. Para obtener más información, consulte Attributes (Atributos).

Los métodos se pueden marcar como inline. Para más información sobre inline, vea Inline Functions (Funciones insertadas).

Los métodos no insertados se pueden usar de forma recursiva dentro del tipo; no es necesario usar explícitamente la palabra clave rec.

Métodos de instancia

Los métodos de instancia se declaran con la palabra clave member y un identificador propio, seguidos de un punto (.) y el nombre y parámetros del método. Como es el caso de los enlaces let, la lista de parámetros puede ser un patrón. Normalmente, los parámetros de método se incluyen entre paréntesis en forma de tupla, que es como aparecen los métodos en F# cuando se crean en otros lenguajes de .NET Framework. Es igualmente común la forma currificada (parámetros separados por espacios) y también se admiten otros patrones.

En el ejemplo siguiente se muestra la definición y el uso de un método de instancia no abstracto.

type SomeType(factor0: int) =
    let factor = factor0
    member this.SomeMethod(a, b, c) = (a + b + c) * factor

    member this.SomeOtherMethod(a, b, c) = this.SomeMethod(a, b, c) * factor

Dentro de los métodos de instancia, no use el identificador propio para acceder a los campos definidos mediante enlaces let. Use el identificador propio al acceder a otros miembros y propiedades.

Métodos estáticos

La palabra clave static se usa para especificar que se puede llamar a un método sin una instancia y que no está asociado a una instancia de objeto. De lo contrario, los métodos son métodos de instancia.

En el ejemplo de la sección siguiente se muestran campos declarados con la palabra clave let, miembros de propiedad declarados con la palabra clave member y un método estático declarado con la palabra clave static.

En el ejemplo siguiente se muestra la definición y el uso de métodos estáticos. Supongamos que estas definiciones de método están en la clase SomeType de la sección anterior.

static member SomeStaticMethod(a, b, c) =
   (a + b + c)

static member SomeOtherStaticMethod(a, b, c) =
   SomeType.SomeStaticMethod(a, b, c) * 100

Métodos abstractos y virtuales

La palabra clave abstract indica que un método tiene una entrada de distribución virtual y es posible que no tenga una definición en la clase. Una entrada de distribución virtual es una entrada en una tabla de funciones mantenida internamente que se usa en tiempo de ejecución para buscar llamadas de función virtual en un tipo orientado a objetos. El mecanismo de distribución virtual es el mecanismo que implementa el polimorfismo, una característica importante de la programación orientada a objetos. Una clase que tiene al menos un método abstracto sin una definición es una clase abstracta, lo que significa que no se pueden crear instancias de esa clase. Para obtener más información sobre las clases abstractas, consulte Clases abstractas.

Las declaraciones de método abstracto no incluyen un cuerpo del método. En su lugar, el nombre del método va seguido de dos puntos (:) y una signatura de tipo para el método. La signatura de tipo de un método es la misma que la que muestra IntelliSense cuando se pausa el puntero del mouse sobre un nombre de método en el editor de Visual Studio Code, pero sin nombres de parámetro. El intérprete (fsi.exe) también muestra las signaturas de tipo cuando se trabaja de forma interactiva. La signatura de tipo de un método se forma al enumerar los tipos de los parámetros, seguidos del tipo de valor devuelto, con símbolos separadores adecuados. Los parámetros currificados se separan con -> y los parámetros de tupla, con *. El valor devuelto siempre se separa de los argumentos con un símbolo ->. Se pueden usar paréntesis para agrupar parámetros complejos, como cuando un tipo de función es un parámetro, o para indicar que una tupla se trata como un único parámetro, en lugar de como dos.

También puede proporcionar definiciones predeterminadas a los métodos abstractos si agrega la definición a la clase y usa la palabra clave default, como se muestra en el bloque de sintaxis de este tema. Un método abstracto que tiene una definición en la misma clase es equivalente a un método virtual en otros lenguajes de .NET Framework. Tanto si existe una definición como si no, la palabra clave abstract crea una entrada de distribución en la tabla de funciones virtuales para la clase.

Independientemente de si una clase base implementa sus métodos abstractos, las clases derivadas pueden proporcionar implementaciones de métodos abstractos. Para implementar un método abstracto en una clase derivada, defina un método que tenga el mismo nombre y signatura en la clase derivada, pero use la palabra clave override o default y proporcione el cuerpo del método. Las palabras clave override y default significan exactamente lo mismo. Use override si el nuevo método invalida una implementación de clase base; use default cuando cree una implementación en la misma clase que la declaración abstracta original. No use la palabra clave abstract en el método que implementa el método que se declaró abstracto en la clase base.

En el ejemplo siguiente se muestra un método abstracto Rotate que tiene una implementación predeterminada, el equivalente de un método virtual de .NET Framework.

type Ellipse(a0: float, b0: float, theta0: float) =
    let mutable axis1 = a0
    let mutable axis2 = b0
    let mutable rotAngle = theta0
    abstract member Rotate: float -> unit
    default this.Rotate(delta: float) = rotAngle <- rotAngle + delta

En el ejemplo siguiente se muestra una clase derivada que invalida un método de clase base. En este caso, la invalidación cambia el comportamiento para que el método no haga nada.

type Circle(radius: float) =
    inherit Ellipse(radius, radius, 0.0)
    // Circles are invariant to rotation, so do nothing.
    override this.Rotate(_) = ()

Métodos sobrecargados

Los métodos sobrecargados son aquellos que tienen nombres idénticos en un tipo determinado, pero que incluyen argumentos diferentes. En F# suelen usarse argumentos opcionales, en lugar de métodos sobrecargados. Aun así, se permiten los métodos sobrecargados en el lenguaje, siempre y cuando los argumentos tengan forma de tupla, en lugar de forma currificada.

Argumentos opcionales

A partir de F# 4.1, también puede haber argumentos opcionales con un valor de parámetro predeterminado en los métodos. Esto facilita la interoperación con código de C#. En el ejemplo siguiente se muestra la sintaxis:

open System.Runtime.InteropServices
// A class with a method M, which takes in an optional integer argument.
type C() =
    member _.M([<Optional; DefaultParameterValue(12)>] i) = i + 1

Tenga en cuenta que el valor que se pasa para DefaultParameterValue debe coincidir con el tipo de entrada. En el ejemplo anterior, es int. Si se intenta pasar un valor no entero en DefaultParameterValue, se producirá un error de compilación.

Ejemplo: propiedades y métodos

El ejemplo siguiente contiene un tipo con ejemplos de campos, funciones privadas, propiedades y un método estático.

type RectangleXY(x1: float, y1: float, x2: float, y2: float) =
    // Field definitions.
    let height = y2 - y1
    let width = x2 - x1
    let area = height * width
    // Private functions.
    static let maxFloat (x: float) (y: float) = if x >= y then x else y
    static let minFloat (x: float) (y: float) = if x <= y then x else y
    // Properties.
    // Here, "this" is used as the self identifier,
    // but it can be any identifier.
    member this.X1 = x1
    member this.Y1 = y1
    member this.X2 = x2
    member this.Y2 = y2
    // A static method.
    static member intersection(rect1: RectangleXY, rect2: RectangleXY) =
        let x1 = maxFloat rect1.X1 rect2.X1
        let y1 = maxFloat rect1.Y1 rect2.Y1
        let x2 = minFloat rect1.X2 rect2.X2
        let y2 = minFloat rect1.Y2 rect2.Y2

        let result: RectangleXY option =
            if (x2 > x1 && y2 > y1) then
                Some(RectangleXY(x1, y1, x2, y2))
            else
                None

        result

// Test code.
let testIntersection =
    let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
    let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
    let r3: RectangleXY option = RectangleXY.intersection (r1, r2)

    match r3 with
    | Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
    | None -> printfn "No intersection found."

testIntersection

Consulte también