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_Zero
astatic 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émuIEnumerable<'T>
omezení . - Při změně
member this.Sum
namember 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ů.