Share via


Estensioni di tipo (F#)

Le estensioni dei tipi consentono di aggiungere nuovi membri a un tipo di oggetto definito in precedenza.

// Intrinsic extension.
type typename with
   member self-identifier.member-name =
      body
   ...
   [ end ]

// Optional extension.
type typename with
   member self-identifier.member-name =
      body
   ...
   [ end ]

Note

Vi sono due forme di estensioni, con sintassi e comportamento leggermente diversi. Un'estensione intrinseca è un'estensione inclusa nello stesso spazio dei nomi o modulo, nello stesso file di origine e nello stesso assembly (DLL o file eseguibile) del tipo esteso. Un'estensione facoltativa è un'estensione che si trova al di fuori del modulo, dello spazio dei nomi o dell'assembly originale del tipo esteso. Le estensioni intrinseche appaiono nel tipo quando il tipo viene esaminato tramite reflection, mentre le estensioni facoltative no. Le estensioni facoltative devono trovarsi nei moduli e sono incluse nell'ambito solo quando il modulo che contiene l'estensione è aperto.

Nella sintassi precedente typename rappresenta il tipo esteso. Qualsiasi tipo a cui è possibile accedere può essere esteso, ma il nome del tipo deve essere un nome di tipo effettivo, non un'abbreviazione del tipo. È possibile definire più membri in un'estensione del tipo. L'elemento self-identifier rappresenta l'istanza dell'oggetto richiamato, come nei normali membri.

La parola chiave end è facoltativa nella sintassi leggera.

I membri definiti nelle estensioni del tipo possono essere utilizzati come gli altri membri in un tipo di classe. Analogamente agli altri membri, possono essere membri statici o di istanza. Questi metodi sono noti anche come metodi di estensione, le proprietà sono note come proprietà di estensione e così via. I membri delle estensioni facoltative vengono compilati in membri statici per i quali l'istanza dell'oggetto viene passata in modo implicito come primo parametro. Tuttavia, agiscono come se fossero membri di istanza o membri statici a seconda di come vengono dichiarati. I membri impliciti delle estensioni sono inclusi come membri del tipo e possono essere utilizzati senza restrizione.

I metodi di estensione non possono essere metodi virtuali o astratti. Possono eseguire l'overload di altri metodi dello stesso nome, ma il compilatore assegna la priorità ai metodi di estensione nel caso di una chiamata ambigua.

Se per un tipo sono presenti più estensioni di tipo intrinseche, tutti i membri devono essere univoci. Per le estensioni di tipo facoltative, i membri delle diverse estensioni di tipo per uno stesso tipo possono avere nomi uguali. Si verificano errori di ambiguità solo se il codice client apre due ambiti diversi che definiscono gli stessi nomi di membro.

Nell'esempio seguente un tipo in un modulo ha un'estensione di tipo intrinseca. Per il codice client esterno al modulo, l'estensione di tipo appare come normale membro del tipo a tutti gli effetti.

module MyModule1 =

    // Define a type. 
    type MyClass() =
      member this.F() = 100

    // Define type extension. 
    type MyClass with 
       member this.G() = 200

module MyModule2 =
   let function1 (obj1: MyModule1.MyClass) =
      // Call an ordinary method.
      printfn "%d" (obj1.F())
      // Call the extension method.
      printfn "%d" (obj1.G())

È possibile utilizzare estensioni di tipo intrinseche per separare la definizione di un tipo in sezioni. Questa operazione può essere utile nella gestione di definizioni del tipo di grandi dimensioni, ad esempio per tenere il codice generato dal compilatore separato dal codice creato dallo sviluppatore o per raggruppare il codice creato da persone diverse o associato a funzionalità diverse.

Nell'esempio seguente viene utilizzata un'estensione di tipo facoltativa per estendere il tipo System.Int32 con un metodo di estensione FromString che chiama il membro statico Parse. Il metodo testFromString indica che il nuovo membro viene chiamato come qualsiasi membro di istanza.

// Define a new member method FromString on the type Int32. 
type System.Int32 with 
    member this.FromString( s : string ) =
       System.Int32.Parse(s)

let testFromString str =  
    let mutable i = 0
    // Use the extension method.
    i <- i.FromString(str)
    printfn "%d" i

testFromString "500"

Il nuovo membro di istanza apparirà come qualsiasi altro metodo del tipo Int32 in IntelliSense, ma solo quando il modulo che contiene l'estensione è aperto o incluso in altro modo nell'ambito.

Metodi di estensione generici

Prima di F# 3.1, il compilatore F# non supportava l'utilizzo dei metodi di estensione di tipo C# con una variabile di tipo generico, un tipo di matrice, un tipo di tupla o un tipo di funzione F# come il parametro "this". F# 3.1 supporta l'utilizzo di tali membri di estensione.

Ad esempio, nel codice F# 3.1, è possibile utilizzare i metodi di estensione con firme simili alla sintassi seguente di C#:

static member Method<T>(this T input, T other)

Questo approccio risulta particolarmente utile quando il parametro di tipo generico è vincolato. Inoltre, è ora possibile dichiarare i membri di estensione come questo nel codice F# e definire un set di metodi di estensione aggiuntivo, semanticamente completo. In F#, in genere si definiscono i membri di estensione come nel seguente esempio:

type seq<’T> with
    /// Repeat each element of the sequence n times
    member xs.RepeatElements(n: int) =
        seq { for x in xs do for i in 1 .. n do yield x }

Tuttavia, per un tipo generico, la variabile di tipo non può essere vincolata. È ora possibile dichiarare un membro di estensione di tipo C# in F# per aggirare questa limitazione. Quando si unisce questo tipo di dichiarazione alla funzionalità inline di F#, è possibile presentare algoritmi generici come membri di estensione.

Si consideri la dichiarazione seguente:

[<Extension>]
type ExtraCSharpStyleExtensionMethodsInFSharp () =
    [<Extension>]
    static member inline Sum(xs: seq<’T>) = Seq.sum xs

Tramite questa dichiarazione, è possibile scrivere codice simile al seguente.

let listOfIntegers = [ 1 .. 100 ]
let listOfBigIntegers = [ 1I to 100I ]
let sum1 = listOfIntegers.Sum()
let sum2 = listOfBigIntegers.Sum()

In questo codice, lo stesso codice aritmetico generico viene applicato agli elenchi di due tipi senza overload, definendo un singolo membro di estensione.

Vedere anche

Altre risorse

Riferimenti per il linguaggio F#

Membri (F#)