Metodi (F#)

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

// 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 self-identifier.method-name : type-signature

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

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

Note

Nella sintassi precedente è possibile vedere le diverse forme di definizioni e dichiarazioni di metodi. In corpi di metodi più lunghi un'interruzione di riga segue il segno di uguale (=) e all'intero corpo del metodo viene applicato un rientro.

Gli attributi possono essere applicati a qualsiasi dichiarazione di metodo. Essi precedono la sintassi per una definizione di metodo e vengono in genere elencati in una riga distinta. Per ulteriori informazioni, vedere Attributi (F#).

I metodi possono essere contrassegnati come inline. Per informazioni su inline, vedere Funzioni inline (F#).

I metodi non inline possono essere utilizzati in modo ricorsivo all'interno del tipo; non è più necessario utilizzare in modo esplicito la parola chiave rec.

Metodi di istanza

I metodi di istanza vengono dichiarati con la parola chiave member e un elemento self-identifier, seguito da un punto (.), nonché il nome e parametri del metodo. Come nel caso delle associazioni let, parameter-list può essere un criterio. In genere, i parametri del metodo vengono racchiusi tra parentesi in formato di tupla, che corrisponde al modo in cui i metodi vengono visualizzati in F# quando vengono creati in altri linguaggi .NET Framework. Anche il formato sottoposto a currying (parametri separati da spazi) è tuttavia comune e sono supportati inoltre altri modelli.

Nell'esempio seguente vengono illustrati la definizione e l'utilizzo 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

Nei metodi di istanza non utilizzare l'elemento self-identifier per accedere ai campi definiti tramite associazioni let. Utilizzare tale elemento quando si accede ad altri membri e proprietà.

Metodi statici

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

Nell'esempio nella sezione successiva vengono illustrati campi dichiarati con la parola chiave let, membri di proprietà dichiarati con la parola chiave member e un metodo statico dichiarato con la parola chiave static.

Nell'esempio seguente vengono illustrati la definizione e l'utilizzo di metodi statici. Si presupponga che queste definizioni di metodo siano incluse nella classe SomeType della 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 dispone di uno slot di invio virtuale e potrebbe non disporre di una definizione nella classe. Uno slot di invio virtuale è una voce in una tabella di funzioni gestita a livello interno e utilizzata al runtime per cercare chiamate di funzioni virtuali in un tipo orientato agli oggetti. Il meccanismo di invio virtuale è il meccanismo che implementa il polimorfismo, un'importante funzionalità della programmazione orientata a oggetti. Una classe che dispone almeno di un metodo astratto senza una definizione è una classe astratta, che significa che non è possibile creare istanze di tale classe. Per ulteriori informazioni sulle classi astratte, vedere Classi astratte (F#).

Le dichiarazioni di metodi astratti non includono un corpo del metodo. Il nome del metodo è invece seguito dai due punti (:) e da una firma del tipo per il metodo. La firma del tipo di un metodo corrisponde a quella visualizzata da IntelliSense quando il puntatore del mouse si posiziona sul nome di un metodo nell'editor del codice di Visual Studio, ad eccezione del fatto che non sono presenti i nomi dei parametri. Le firme dei tipi vengono visualizzate anche dall'interprete, fsi.exe, quando si lavora in modalità interattiva. La firma del tipo di un metodo viene formata elencando i tipi dei parametri, seguiti dal tipo restituito, con i simboli dei separatori appropriati. I parametri sottoposti a currying sono separati da -> e quelli in formato di tupla da *. Il valore restituito è sempre separato dagli argomenti da un simbolo ->. È possibile utilizzare le parentesi per raggruppare parametri complessi, ad esempio quando un tipo di funzione è un parametro, o per indicare quando una tupla viene trattata come singolo parametro anziché come due parametri.

È inoltre possibile assegnare ai metodi astratti definizioni predefinite aggiungendo la definizione alla classe e utilizzando la parola chiave default, come illustrato nel blocco di sintassi in questo argomento. Un metodo astratto che dispone di una definizione nella stessa classe è equivalente a un metodo virtuale in altri linguaggi .NET Framework. Indipendentemente dalla presenza o meno di una definizione, la parola chiave abstract crea un nuovo slot di invio nella tabella di funzioni virtuali per la classe.

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

Nell'esempio seguente viene illustrato un metodo Rotate astratto che dispone di 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 della classe di base. In questo caso, tramite l'override viene modificato il comportamento in modo che il metodo non esegua 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 tipo specifico, ma che dispongono di argomenti diversi. In F# al posto dei metodi di overload vengono in genere utilizzati argomenti facoltativi. I metodi di overload sono tuttavia consentiti nel linguaggio, a condizione che gli argomenti siano in formato di tupla e non sottoposti a currying.

Esempio: proprietà e metodi

Nell'esempio seguente è illustrato un tipo che include 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

Vedere anche

Altre risorse

Membri (F#)