Sdílet prostřednictvím


Rozšíření typů

Rozšíření typů (označovaná také jako rozšíření) jsou řada funkcí, které umožňují přidávat nové členy do dříve definovaného typu objektu. Jedná se o tři funkce:

  • Rozšíření vnitřních typů
  • Rozšíření volitelného typu
  • Metody rozšíření

Každý z nich se dá použít v různých scénářích a má různé kompromisy.

Syntaxe

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

Rozšíření vnitřních typů

Vnitřní rozšíření typu je rozšíření typu, které rozšiřuje uživatelem definovaný typ.

Vnitřní přípony typů musí být definovány ve stejném souboru a ve stejném oboru názvů nebo modulu jako typ, který rozšiřují. Každá jiná definice bude mít za následek volitelná rozšíření typu.

Rozšíření vnitřních typů jsou někdy čistější způsob, jak oddělit funkce od deklarace typu. Následující příklad ukazuje, jak definovat vnitřní typ rozšíření:

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

Použití rozšíření typu umožňuje oddělit každou z následujících možností:

  • Variant Deklarace typu
  • Funkce tisku Variant třídy v závislosti na jeho "tvaru"
  • Způsob, jak získat přístup k funkcím tisku pomocí objektového stylu .-notation

Jedná se o alternativu k definování všeho jako člena na Variant. I když to není ze své podstaty lepší přístup, může to být čistější reprezentace funkcí v některých situacích.

Vnitřní rozšíření typů se kompilují jako členové typu, které rozšiřují, a zobrazují se na typu, když je typ zkoumán reflexí.

Rozšíření volitelného typu

Volitelné rozšíření typu je rozšíření, které se zobrazuje mimo původní modul, obor názvů nebo sestavení rozšířeného typu.

Volitelná rozšíření typu jsou užitečná pro rozšíření typu, který jste sami nedefinovali. Příklad:

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
        }

Teď máte přístup, RepeatElements jako by byl členem IEnumerable<T> , pokud Extensions je modul otevřen v oboru, ve který pracujete.

Nepovinná rozšíření se při zkoumání reflexí nezobrazují u rozšířeného typu. Volitelná rozšíření musí být vmodulech

Volitelné členy rozšíření jsou zkompilovány na statické členy, pro které je instance objektu předána implicitně jako první parametr. Fungují ale stejně jako členové instance nebo statické členy podle toho, jak jsou deklarovány.

Volitelné členy rozšíření také nejsou viditelné pro uživatele jazyka C# nebo Visual Basic. Mohou být využity pouze v jiném kódu jazyka F#.

Obecné omezení vnitřních a volitelných rozšíření typů

Rozšíření typu je možné deklarovat u obecného typu, kde je proměnná typu omezena. Požadavek je, aby omezení deklarace rozšíření odpovídalo omezení deklarovaného typu.

I když jsou však omezení spárována mezi deklarovaným typem a rozšířením typu, je možné omezení odvodit tělem rozšířeného člena, který ukládá jiný požadavek na parametr typu než deklarovaný typ. Příklad:

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

Neexistuje způsob, jak tento kód získat pro práci s volitelným rozšířením typu:

  • Jak je tomu, člen má jiné omezení 'T (static member get_Zeroastatic member (+)) než to, Sum co rozšíření typu definuje.
  • Úprava rozšíření typu tak, aby měla stejné omezení, jako Sum již nebude odpovídat definovanému IEnumerable<'T>omezení .
  • Při změně member this.Sum na member inline this.Sum se zobrazí chyba, že se neshodují omezení typu.

To, co je žádoucí, jsou statické metody, které "plovávají v prostoru" a dají se prezentovat, jako by rozšířily typ. To je místo, kde jsou metody rozšíření nezbytné.

Metody rozšíření

Nakonec lze metody rozšíření (někdy označované jako "členy rozšíření stylu jazyka C#") deklarovat v jazyce F# jako statickou metodu člena třídy.

Metody rozšíření jsou užitečné, pokud chcete definovat rozšíření pro obecný typ, který omezí proměnnou typu. Příklad:

namespace Extensions

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

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

Při použití se tento kód zobrazí, jako by Sum byl definován na IEnumerable<T>, pokud Extensions byl otevřen nebo je v oboru.

Aby bylo rozšíření k dispozici pro VB.NET kód, vyžaduje se na úrovni sestavení další ExtensionAttribute :

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

Další poznámky

Rozšíření typů mají také následující atributy:

  • Lze rozšířit libovolný typ, ke kterému lze získat přístup.
  • Vnitřní a volitelná rozšíření typů mohou definovat libovolný typ členu, nejen metody. Vlastnosti rozšíření jsou také možné, například.
  • Token self-identifier v syntaxi představuje instanci vyvolaného typu, stejně jako běžné členy.
  • Rozšířené členy můžou být statické členy nebo členy instance.
  • Proměnné typu v rozšíření typu musí odpovídat omezením deklarovaného typu.

Pro rozšíření typů existují také následující omezení:

  • Rozšíření typů nepodporují virtuální ani abstraktní metody.
  • Rozšíření typů nepodporují metody přepsání jako rozšíření.
  • Rozšíření typů nepodporují staticky vyřešené parametry typu.
  • Rozšíření volitelných typů nepodporují konstruktory jako rozšíření.
  • Přípony typů nelze definovat u zkratek typů.
  • Přípony typů nejsou platné pro byref<'T> (i když je lze deklarovat).
  • Rozšíření typů nejsou platná pro atributy (i když je lze deklarovat).
  • Můžete definovat rozšíření, která přetíží jiné metody stejného názvu, ale kompilátor F# dává přednost metodám bez rozšíření, pokud existuje nejednoznačné volání.

Pokud pro jeden typ existuje více vnitřních rozšíření typu, musí být všichni členové jedineční. U volitelných rozšíření typů můžou mít členové v různých typech rozšíření stejného typu stejné názvy. K chybám nejednoznačnosti dochází pouze v případě, že kód klienta otevře dva různé obory, které definují stejné názvy členů.

Viz také