Delen via


Type-extensies

Typeextensies (ook wel uitbreidingen genoemd) zijn een reeks functies waarmee u nieuwe leden kunt toevoegen aan een eerder gedefinieerd objecttype. De drie functies zijn:

  • Intrinsiek type extensies
  • Optionele typeextensies
  • Extensiemethoden

Elk kan worden gebruikt in verschillende scenario's en heeft verschillende compromissen.

Syntaxis

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

Intrinsiek type extensies

Een intrinsieke typeextensie is een typeextensie die een door de gebruiker gedefinieerd type uitbreidt.

Intrinsieke typeextensies moeten worden gedefinieerd in hetzelfde bestand en in dezelfde naamruimte of module als het type dat ze uitbreiden. Elke andere definitie resulteert in optionele typeextensies.

Intrinsieke typeuitbreidingen zijn soms een schonere manier om functionaliteit van de typedeclaratie te scheiden. In het volgende voorbeeld ziet u hoe u een intrinsieke typeextensie definieert:

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

Met behulp van een typeextensie kunt u elk van de volgende items scheiden:

  • De declaratie van een Variant type
  • Functionaliteit om de klasse af te drukken, afhankelijk van de Variant 'shape'
  • Een manier om toegang te krijgen tot de afdrukfunctionaliteit met objectstijl - .notatie

Dit is een alternatief voor het definiëren van alles als lid.Variant Hoewel het geen inherent betere benadering is, kan het in sommige situaties een schonere weergave van functionaliteit zijn.

Intrinsieke typeextensies worden gecompileerd als leden van het type dat ze uitbreiden en worden weergegeven op het type wanneer het type wordt onderzocht door reflectie.

Optionele typeextensies

Een optionele typeextensie is een extensie die buiten de oorspronkelijke module, naamruimte of assembly van het type wordt uitgebreid.

Optionele typeextensies zijn handig voor het uitbreiden van een type dat u niet zelf hebt gedefinieerd. Voorbeeld:

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
        }

U kunt nu toegang krijgen RepeatElements alsof deze lid is van IEnumerable<T> zolang de Extensions module is geopend in het bereik waarin u werkt.

Optionele extensies worden niet weergegeven op het uitgebreide type wanneer ze door reflectie worden onderzocht. Optionele extensies moeten zich in modules bevinden en ze bevinden zich alleen binnen het bereik wanneer de module met de extensie is geopend of anderszins binnen het bereik valt.

Optionele extensieleden worden gecompileerd naar statische leden waarvoor het objectexemplaren impliciet worden doorgegeven als de eerste parameter. Ze fungeren echter alsof ze instantieleden of statische leden zijn op basis van hoe ze worden gedeclareerd.

Optionele extensieleden zijn ook niet zichtbaar voor C# of Visual Basic-gebruikers. Ze kunnen alleen worden gebruikt in andere F#-code.

Algemene beperking van intrinsieke en optionele typeextensies

Het is mogelijk om een typeextensie te declareren voor een algemeen type waarbij de typevariabele wordt beperkt. De vereiste is dat de beperking van de uitbreidingsdeclaratie overeenkomt met de beperking van het gedeclareerde type.

Zelfs wanneer beperkingen worden vergeleken tussen een gedeclareerd type en een typeextensie, is het echter mogelijk dat een beperking wordt afgeleid door de hoofdtekst van een uitgebreid lid dat een andere vereiste voor de typeparameter oplegt dan het gedeclareerde type. Voorbeeld:

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

Er is geen manier om deze code te laten werken met een optionele typeextensie:

  • Zoals het is, heeft het Sum lid een andere beperking op 'T (static member get_Zero en static member (+)) dan wat de typeextensie definieert.
  • Als u de typeextensie wijzigt zodat deze dezelfde beperking heeft als Sum die niet meer overeenkomt met de gedefinieerde beperking IEnumerable<'T>.
  • Als member this.Sum u dit member inline this.Sum wijzigt, treedt er een fout op dat typebeperkingen niet overeenkomen.

Wat gewenst is, zijn statische methoden die 'in ruimte zweven' en kunnen worden weergegeven alsof ze een type uitbreiden. Hier worden uitbreidingsmethoden nodig.

Extensiemethoden

Ten slotte kunnen extensiemethoden (ook wel 'C#-stijlextensieleden' genoemd) in F# worden gedeclareerd als een statische lidmethode voor een klasse.

Extensiemethoden zijn handig als u extensies wilt definiëren voor een algemeen type dat de typevariabele beperkt. Voorbeeld:

namespace Extensions

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

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

Wanneer deze code wordt gebruikt, wordt deze weergegeven alsof Sum deze is gedefinieerd, IEnumerable<T>zolang Extensions deze is geopend of binnen het bereik valt.

Voordat de extensie beschikbaar is voor VB.NET code, is er een extra ExtensionAttribute vereist op assemblyniveau:

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

Overige opmerkingen

Typeextensies hebben ook de volgende kenmerken:

  • Elk type dat kan worden geopend, kan worden uitgebreid.
  • Intrinsieke en optionele typeextensies kunnen elk lidtype definiëren, niet alleen methoden. Uitbreidingseigenschappen zijn bijvoorbeeld ook mogelijk.
  • Het self-identifier token in de syntaxis vertegenwoordigt het exemplaar van het type dat wordt aangeroepen, net als gewone leden.
  • Uitgebreide leden kunnen statisch of exemplaarleden zijn.
  • Typevariabelen voor een typeextensie moeten overeenkomen met de beperkingen van het gedeclareerde type.

De volgende beperkingen gelden ook voor typeextensies:

  • Typeextensies bieden geen ondersteuning voor virtuele of abstracte methoden.
  • Typeextensies bieden geen ondersteuning voor onderdrukkingsmethoden als uitbreidingen.
  • Typeextensies bieden geen ondersteuning voor parameters voor statisch opgeloste typen.
  • Optionele typeextensies bieden geen ondersteuning voor constructors als uitbreidingen.
  • Typeextensies kunnen niet worden gedefinieerd voor type afkortingen.
  • Typeextensies zijn niet geldig voor byref<'T> (hoewel ze kunnen worden gedeclareerd).
  • Typeextensies zijn niet geldig voor kenmerken (hoewel ze kunnen worden gedeclareerd).
  • U kunt extensies definiëren die andere methoden van dezelfde naam overbelasten, maar de F#-compiler geeft voorkeur aan niet-extensiemethoden als er sprake is van een dubbelzinnige aanroep.

Als er ten slotte meerdere intrinsieke typeextensies voor één type bestaan, moeten alle leden uniek zijn. Voor optionele typeextensies kunnen leden in verschillende typeextensies met hetzelfde type dezelfde namen hebben. Dubbelzinnigheidsfouten treden alleen op als clientcode twee verschillende bereiken opent die dezelfde lidnamen definiëren.

Zie ook