Share via


Typtillägg

Typtillägg (kallas även förökningar) är en uppsättning funktioner som gör att du kan lägga till nya medlemmar i en tidigare definierad objekttyp. De tre funktionerna är:

  • Tillägg av inbyggd typ
  • Valfria typtillägg
  • Tilläggsmetoder

Var och en kan användas i olika scenarier och har olika kompromisser.

Syntax

// Intrinsic and optional extensions
type typename with
    member self-identifier.member-name =
        body
    ...

// Extension methods
open System.Runtime.CompilerServices

[<Extension>]
type Extensions() =
    [<Extension>]
    static member extension-name (ty: typename, [args]) =
        body
    ...

Tillägg av inbyggd typ

Ett tillägg av inbyggd typ är ett typtillägg som utökar en användardefinierad typ.

Tillägg av inbyggd typ måste definieras i samma fil och i samma namnområde eller modul som den typ som de utökar. Alla andra definitioner resulterar i att de är valfria typtillägg.

Tillägg av inbyggd typ är ibland ett renare sätt att skilja funktioner från typdeklarationen. I följande exempel visas hur du definierar ett tillägg av inbyggd typ:

namespace Example

type Variant =
    | Num of int
    | Str of string
  
module Variant =
    let print v =
        match v with
        | Num n -> printf "Num %d" n
        | Str s -> printf "Str %s" s

// Add a member to Variant as an extension
type Variant with
    member x.Print() = Variant.print x

Med hjälp av ett typtillägg kan du separera var och en av följande:

  • Deklarationen av en Variant typ
  • Funktioner för att skriva ut Variant klassen beroende på dess "form"
  • Ett sätt att komma åt utskriftsfunktionen med objektformatet .-notation

Det här är ett alternativ till att definiera allt som medlem på Variant. Även om det inte är en i sig bättre metod kan det vara en renare representation av funktioner i vissa situationer.

Tillägg av inbyggd typ kompileras som medlemmar av den typ som de utökar och visas på typen när typen granskas med reflektion.

Valfria typtillägg

Ett tillägg av valfri typ är ett tillägg som visas utanför den ursprungliga modulen, namnområdet eller sammansättningen av den typ som utökas.

Valfria typtillägg är användbara för att utöka en typ som du inte har definierat själv. Till exempel:

module Extensions

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

Nu kan du komma åt RepeatElements den som om den är medlem IEnumerable<T> i så länge modulen Extensions öppnas i det omfång som du arbetar i.

Valfria tillägg visas inte på den utökade typen när de granskas av reflektion. Valfria tillägg måste finnas i moduler och de finns bara i omfånget när modulen som innehåller tillägget är öppen eller på annat sätt finns i omfånget.

Valfria tilläggsmedlemmar kompileras till statiska medlemmar för vilka objektinstansen implicit skickas som den första parametern. De fungerar dock som om de är instansmedlemmar eller statiska medlemmar beroende på hur de deklareras.

Valfria tilläggsmedlemmar är inte heller synliga för C# eller Visual Basic-användare. De kan bara användas i annan F#-kod.

Allmän begränsning av inbyggda och valfria typtillägg

Det går att deklarera ett typtillägg för en allmän typ där typvariabeln är begränsad. Kravet är att villkoret för tilläggsdeklarationen matchar villkoret för den deklarerade typen.

Men även om begränsningar matchas mellan en deklarerad typ och ett typtillägg är det möjligt att en begränsning härleds av brödtexten för en utökad medlem som ställer ett annat krav på typparametern än den deklarerade typen. Till exempel:

open System.Collections.Generic

// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
    member this.Sum() = Seq.sum this

Det finns inget sätt att få den här koden att fungera med ett valfritt typtillägg:

  • Som det är Sum har medlemmen en annan begränsning för 'T (static member get_Zero och static member (+)) än vad typtillägget definierar.
  • Ändra typtillägget så att det har samma villkor som Sum inte längre matchar den definierade begränsningen för IEnumerable<'T>.
  • Om du ändrar member this.Sum till member inline this.Sum får du ett fel om att typbegränsningarna är matchningsfel.

Vad som önskas är statiska metoder som "flyter i rymden" och kan visas som om de utökar en typ. Det är här tilläggsmetoderna blir nödvändiga.

Tilläggsmetoder

Slutligen kan tilläggsmetoder (kallas ibland "C#-formattilläggsmedlemmar") deklareras i F# som en statisk medlemsmetod i en klass.

Tilläggsmetoder är användbara när du vill definiera tillägg för en allmän typ som begränsar typvariabeln. Till exempel:

namespace Extensions

open System.Collections.Generic
open System.Runtime.CompilerServices

[<Extension>]
type IEnumerableExtensions =
    [<Extension>]
    static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs

När den här koden används visas den som om Sum den har definierats på IEnumerable<T>, så länge som Extensions har öppnats eller finns i omfånget.

För att tillägget ska vara tillgängligt för VB.NET kod krävs ett extra ExtensionAttribute tillägg på sammansättningsnivå:

module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()

Andra kommentarer

Typtillägg har också följande attribut:

  • Alla typer som kan nås kan utökas.
  • Inbyggda och valfria typtillägg kan definiera alla medlemstyper, inte bara metoder. Så tilläggsegenskaper är också möjliga, till exempel.
  • Token self-identifier i syntaxen representerar den instans av typen som anropas, precis som vanliga medlemmar.
  • Utökade medlemmar kan vara statiska medlemmar eller instansmedlemmar.
  • Typvariabler för ett typtillägg måste matcha begränsningarna för den deklarerade typen.

Följande begränsningar finns också för typtillägg:

  • Typtillägg stöder inte virtuella eller abstrakta metoder.
  • Typtillägg stöder inte åsidosättningsmetoder som förhöjda funktioner.
  • Typtillägg stöder inte statiskt lösta typparametrar.
  • Valfria typtillägg stöder inte konstruktorer som förhöjda objekt.
  • Det går inte att definiera typtillägg för typförkortningar.
  • Typtillägg är inte giltiga för byref<'T> (även om de kan deklareras).
  • Typtillägg är inte giltiga för attribut (även om de kan deklareras).
  • Du kan definiera tillägg som överbelastar andra metoder med samma namn, men F#-kompilatorn föredrar icke-tilläggsmetoder om det finns ett tvetydigt anrop.

Om det finns flera inbyggda typtillägg för en typ måste alla medlemmar vara unika. För valfria typtillägg kan medlemmar i olika typtillägg till samma typ ha samma namn. Tvetydighetsfel uppstår endast om klientkoden öppnar två olika omfång som definierar samma medlemsnamn.

Se även