Típuskiterjesztések
A típusbővítmények (más néven kiegészítések) olyan funkciócsaládok, amelyek lehetővé teszik új tagok hozzáadását egy korábban definiált objektumtípushoz. A három funkció a következő:
- Belső típusú bővítmények
- Választható típusbővítmények
- Bővítménymetelyek
Mindegyik különböző forgatókönyvekben használható, és különböző kompromisszumokkal rendelkezik.
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
...
Belső típusú bővítmények
A belső típusú bővítmények olyan típuskiterjesztések, amelyek kiterjesztik a felhasználó által definiált típusokat.
A belső típuskiterjesztéseket ugyanabban a fájlban és ugyanabban a névtérben vagy modulban kell definiálni, mint a kiterjesztett típust. Bármely más definíció azt eredményezi, hogy választható típuskiterjesztések lesznek.
A belső típusbővítmények néha tisztábban elkülönítik a funkciót a típusdeklarációtól. Az alábbi példa bemutatja, hogyan definiálhat belső típusú bővítményt:
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
A típusbővítmények használatával az alábbiakat választhatja el egymástól:
- Típus deklarációja
Variant
- Az osztály nyomtatásának funkciója az
Variant
"alakzattól" függően - A nyomtatási funkciók objektumstílusú
.
-jelöléssel való elérésének módja
Ez egy alternatíva arra, hogy mindent tagként definiáljon.Variant
Bár ez nem eredendően jobb megközelítés, bizonyos helyzetekben tisztábban ábrázolhatja a funkcionalitást.
A belső típusú bővítmények az általuk kiegészített típus tagjaiként vannak lefordítva, és a típuson jelennek meg, amikor a típust tükröződéssel vizsgálják.
Választható típusbővítmények
A választható típusbővítmény olyan bővítmény, amely az eredeti modulon, névtéren vagy a kibővítendő típus szerelvényén kívül jelenik meg.
A választható típusbővítmények olyan típus kibővítése esetén hasznosak, amelyeket ön nem definiált. Példa:
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
}
Most már úgy érheti el RepeatElements
, IEnumerable<T> mintha tagja lenne, ha a Extensions
modul meg van nyitva abban a hatókörben, amelyben dolgozik.
Az opcionális bővítmények nem jelennek meg a kiterjesztett típuson, ha tükröződés alapján vizsgálják meg. Az opcionális bővítményeknek modulokban kell lenniük, és csak akkor vannak hatókörben, ha a bővítményt tartalmazó modul nyitva van, vagy egyébként hatókörben van.
A választható bővítménytagok olyan statikus tagokra lesznek lefordítva, amelyek esetében az objektumpéldány implicit módon lesz átadva első paraméterként. A deklarált módon azonban úgy viselkednek, mintha példánytagok vagy statikus tagok lennének.
A választható bővítménytagok a C# és a Visual Basic felhasználói számára sem láthatók. Ezek csak más F#-kódban használhatók.
A belső és választható típuskiterjesztések általános korlátozása
Típuskiterjesztést deklarálhat egy általános típuson, ahol a típusváltozó korlátozott. A követelmény az, hogy a kiterjesztési deklaráció kényszere megegyezik a deklarált típus kényszerével.
Ha azonban a kényszerek egy deklarált típus és egy típuskiterjesztés között vannak is megfeleltetve, előfordulhat, hogy egy kiterjesztett tag törzse a deklarált típustól eltérő követelményt ír elő a típusparaméterre vonatkozóan. Példa:
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
A kód nem használható opcionális típusbővítménnyel:
- Ahogyan az is, hogy a
Sum
tagra a típusbővítmény által definiálttól eltérő korlátozás van hatással'T
(static member get_Zero
ésstatic member (+)
) a típuskiterjesztésre. - Ha úgy módosítja a típusbővítményt, hogy ugyanazzal a kényszersel rendelkezzen, mint ami
Sum
a továbbiakban nem felel meg a megadott kényszernekIEnumerable<'T>
. member inline this.Sum
A módosításmember this.Sum
olyan hibát eredményez, amely miatt a típuskorlátozások nem egyeznek.
Mi a kívánt statikus metódusok, amelyek "lebegnek a térben", és úgy mutathatók be, mintha egy típust bővítenének. Itt válnak szükségessé a bővítménymetelyek.
Bővítménymetelyek
Végül a bővítménymetódusok (más néven "C# stílusú bővítménytagok") az F#-ban deklarálhatók az osztály statikus tagmetódusaként.
A bővítménymetodusok akkor hasznosak, ha olyan általános típuson szeretné definiálni a bővítményeket, amelyek korlátozzák a típusváltozót. Példa:
namespace Extensions
open System.Collections.Generic
open System.Runtime.CompilerServices
[<Extension>]
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
Használat esetén ez a kód úgy jelenik meg, mintha Sum
meg lett volna adva IEnumerable<T>, mindaddig, amíg Extensions
meg van nyitva vagy hatókörben van.
Ahhoz, hogy a bővítmény elérhető legyen VB.NET kódhoz, a szerelvény szintjén további szükséges ExtensionAttribute
:
module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()
Egyéb megjegyzések
A típusbővítmények az alábbi attribútumokkal is rendelkeznek:
- Bármilyen elérhető típus bővíthető.
- Az intrinsic és az opcionális típusbővítmények bármilyen tagtípust definiálhatnak, nem csak metódusokat. Így például a bővítmény tulajdonságai is lehetségesek.
- A
self-identifier
szintaxisban szereplő jogkivonat a meghívandó típus példányát jelöli, csakúgy, mint a szokásos tagok. - A kiterjesztett tagok lehetnek statikus vagy példánytagok.
- A típuskiterjesztés típusváltozóinak meg kell egyeznie a deklarált típus korlátaival.
A típusbővítményekre a következő korlátozások is vonatkoznak:
- A típusbővítmények nem támogatják a virtuális vagy absztrakt metódusokat.
- A típusbővítmények nem támogatják a felülbírálási módszereket bővítésként.
- A típusbővítmények nem támogatják a statikusan feloldott típusparamétereket.
- Az opcionális típusbővítmények nem támogatják a konstruktorokat bővítésként.
- Típusbővítmények nem definiálhatók típus rövidítéseken.
- A típusbővítmények érvénytelenek
byref<'T>
(bár deklarálhatók). - A típusbővítmények nem érvényesek az attribútumokra (bár deklarálhatók).
- Definiálhat olyan bővítményeket, amelyek túlterhelik az azonos nevű metódusokat, de az F#-fordító előnyben részesíti a nem bővítményes metódusokat, ha nem egyértelmű hívás történik.
Végül, ha egy típushoz több belső típusú bővítmény is létezik, minden tagnak egyedinek kell lennie. Választható típuskiterjesztések esetén az azonos típusú különböző típusú bővítmények tagjainak ugyanazokkal a névvel kell rendelkezniük. Kétértelműségi hibák csak akkor fordulnak elő, ha az ügyfélkód két különböző hatókört nyit meg, amelyek ugyanazokat a tagneveket határozzák meg.