Condividi tramite


Metodi

Un metodo è una funzione associata a un tipo. Nella programmazione orientata agli oggetti, i metodi vengono usati per esporre e implementare le funzionalità e il comportamento di oggetti e tipi.

Sintassi

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

Osservazioni:

Nella sintassi precedente è possibile visualizzare le varie forme di dichiarazioni e definizioni dei metodi. Nei corpi del metodo più lunghi, un'interruzione di riga segue il segno di uguale (=) e il rientro dell'intero corpo del metodo.

Gli attributi possono essere applicati a qualsiasi dichiarazione di metodo. Precedono la sintassi per una definizione di metodo e sono in genere elencate in una riga separata. Per altre informazioni, vedere Attributi.

I metodi possono essere contrassegnati come inline. Per informazioni su inline, vedere Funzioni inline.

I metodi non inline possono essere usati in modo ricorsivo all'interno del tipo; non è necessario usare in modo esplicito la rec parola chiave .

Metodi di istanza

I metodi di istanza vengono dichiarati con la member parola chiave e un identificatore automatico, seguiti da un punto (.) e dal nome e dai parametri del metodo. Come accade per let le associazioni, l'elenco di parametri può essere un modello. In genere, i parametri del metodo vengono racchiusi tra parentesi in un formato di tupla, ovvero il modo in cui i metodi vengono visualizzati in F# quando vengono creati in altri linguaggi .NET Framework. Tuttavia, anche il formato curried (parametri separati da spazi) è comune e sono supportati anche altri modelli.

Nell'esempio seguente viene illustrata la definizione e l'uso di un metodo di istanza non astratto.

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

All'interno dei metodi di istanza, non usare l'identificatore self per accedere ai campi definiti tramite le associazioni let. Usare l'identificatore auto durante l'accesso ad altri membri e proprietà.

Metodi statici

La parola chiave static viene utilizzata per specificare che un metodo può essere chiamato senza un'istanza di e non è associato a un'istanza dell'oggetto. In caso contrario, i metodi sono metodi di istanza.

L'esempio nella sezione successiva mostra i campi dichiarati con la let parola chiave , i membri della proprietà dichiarati con la member parola chiave e un metodo statico dichiarato con la static parola chiave .

Nell'esempio seguente viene illustrata la definizione e l'uso di metodi statici. Si supponga che queste definizioni di metodo si trovino nella SomeType classe nella sezione precedente.

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

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

Metodi virtuali e astratti

La parola chiave abstract indica che un metodo ha uno slot di invio virtuale e potrebbe non avere una definizione nella classe . Uno slot di invio virtuale è una voce in una tabella di funzioni gestita internamente che viene usata in fase di esecuzione per cercare le chiamate di funzione virtuale in un tipo orientato agli oggetti. Il meccanismo di distribuzione virtuale è il meccanismo che implementa il polimorfismo, una caratteristica importante della programmazione orientata agli oggetti. Una classe che dispone di almeno un metodo astratto senza una definizione è una classe astratta, il che significa che nessuna istanza può essere creata di tale classe. Per altre informazioni sulle classi astratte, vedere Classi astratte.

Le dichiarazioni di metodo astratte non includono un corpo del metodo. Al contrario, il nome del metodo è seguito da due punti (:) e una firma di tipo per il metodo. La firma del tipo di un metodo è identica a quella mostrata da IntelliSense quando si sospende il puntatore del mouse su un nome di metodo nell'editor di Visual Studio Code, tranne senza nomi di parametri. Le firme di tipo vengono visualizzate anche dall'interprete, fsi.exe, quando si lavora in modo interattivo. La firma del tipo di un metodo è costituita dall'elenco dei tipi dei parametri, seguiti dal tipo restituito, con simboli separatori appropriati. I parametri curried sono separati da -> e i parametri della tupla sono separati da *. Il valore restituito è sempre separato dagli argomenti da un -> simbolo. Le parentesi possono essere usate per raggruppare parametri complessi, ad esempio quando un tipo di funzione è un parametro o per indicare quando una tupla viene considerata come un singolo parametro anziché come due parametri.

È anche possibile assegnare definizioni predefinite ai metodi astratti aggiungendo la definizione alla classe e usando la default parola chiave , come illustrato nel blocco di sintassi in questo argomento. Un metodo astratto con una definizione nella stessa classe equivale a un metodo virtuale in altri linguaggi .NET Framework. Indipendentemente dal fatto che esista o meno una definizione, la abstract parola chiave crea un nuovo slot dispatch nella tabella delle funzioni virtuali per la classe .

Indipendentemente dal fatto che una classe base implementi i relativi metodi astratti, le classi derivate possono fornire implementazioni di metodi astratti. Per implementare un metodo astratto in una classe derivata, definire un metodo con lo stesso nome e firma nella classe derivata, ad eccezione dell'uso della override parola chiave o default e fornire il corpo del metodo. Le parole chiave override e default indicano esattamente la stessa cosa. Utilizzare se il nuovo metodo esegue l'override di un'implementazione della classe base. Utilizzare overridedefault quando si crea un'implementazione nella stessa classe della dichiarazione astratta originale. Non usare la abstract parola chiave nel metodo che implementa il metodo dichiarato astrazione nella classe di base.

Nell'esempio seguente viene illustrato un metodo Rotate astratto con un'implementazione predefinita, equivalente a un metodo virtuale .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

Nell'esempio seguente viene illustrata una classe derivata che esegue l'override di un metodo di classe base. In questo caso, l'override modifica il comportamento in modo che il metodo non esegue alcuna operazione.

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

Metodi di overload

I metodi di overload sono metodi con nomi identici in un determinato tipo, ma con argomenti diversi. In F# gli argomenti facoltativi vengono in genere usati anziché metodi di overload. Tuttavia, i metodi di overload sono consentiti nel linguaggio, a condizione che gli argomenti siano in forma di tupla, non curried form.

Argomenti facoltativi

A partire da F# 4.1, è anche possibile avere argomenti facoltativi con un valore di parametro predefinito nei metodi. Ciò consente di facilitare l'interoperabilità con il codice C#. L'esempio seguente illustra la sintassi:

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

Si noti che il valore passato per DefaultParameterValue deve corrispondere al tipo di input. Nell'esempio precedente si tratta di un oggetto int. Il tentativo di passare un valore non intero in DefaultParameterValue genera un errore di compilazione.

Esempio: proprietà e metodi

L'esempio seguente contiene un tipo con esempi di campi, funzioni private, proprietà e un metodo statico.

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

Vedi anche