Generikus
Az F#-függvényértékek, metódusok, tulajdonságok és összesítési típusok, például osztályok, rekordok és diszkriminált egyesítések általánosak lehetnek. Az általános szerkezetek legalább egy típusparamétert tartalmaznak, amelyet általában az általános szerkezet felhasználója ad meg. Az általános függvények és típusok lehetővé teszik olyan kódok írását, amelyek különböző típusokkal működnek anélkül, hogy minden típushoz meg kell ismételni a kódot. A kód általánossá tétele egyszerű lehet az F#-ban, mivel a kód gyakran implicit módon arra következtet, hogy a fordító típuskövetkeztetési és automatikus általánosítási mechanizmusai általánosak.
Syntax
// Explicitly generic function.
let function-name<type-parameters> parameter-list =
function-body
// Explicitly generic method.
[ static ] member object-identifier.method-name<type-parameters> parameter-list [ return-type ] =
method-body
// Explicitly generic class, record, interface, structure,
// or discriminated union.
type type-name<type-parameters> type-definition
Megjegyzések
Egy explicit módon általános függvény vagy típus deklarációja hasonló a nem általános függvényhez vagy típushoz, kivéve a típusparaméterek specifikációját (és használatát) a függvény vagy típus neve utáni szögletes zárójelekben.
A deklarációk gyakran implicit módon általánosak. Ha nem adja meg teljes mértékben a függvény vagy típus megírásához használt összes paraméter típusát, a fordító megpróbálja kikövetkeztetni az egyes paraméterek, értékek és változók típusát a megírt kódból. További információ: Típuskövetkeztetés. Ha a típus vagy függvény kódja más módon nem korlátozza a paraméterek típusait, a függvény vagy a típus implicit módon általános. Ezt a folyamatot automatikus általánosításnak nevezik. Az automatikus általánosításnak bizonyos korlátai vannak. Ha például az F#-fordító nem tudja kikövetkeztetni egy általános szerkezet típusait, a fordító egy olyan hibát jelez, amely az értékkorlátozás nevű korlátozásra hivatkozik. Ebben az esetben előfordulhat, hogy valamilyen típusú széljegyzetet kell hozzáadnia. Az automatikus általánosításról és az értékkorlátozásról, valamint a kód a probléma megoldásához történő módosításáról az Automatikus általánosítás című témakörben talál további információt.
Az előző szintaxisban a típusparaméterek ismeretlen típusokat képviselő paraméterek vesszővel tagolt listája, amelyek mindegyike egyetlen idézőjellel kezdődik, opcionálisan egy kényszerkikötéssel, amely tovább korlátozza az adott típusparaméterhez használható típusokat. A különböző típusú kényszerfeltételek szintaxisát és a kényszerekkel kapcsolatos egyéb információkat a Kényszerek című témakörben talál.
A szintaxisban szereplő típusdefiníció megegyezik a nem általános típus típusdefinícióival. Tartalmazza az osztálytípus konstruktorparamétereit, egy választható as
záradékot, az egyenlőségszimbólumot, a rekordmezőket, a inherit
záradékot, a diszkriminált egyesítő lehetőségeket, let
valamint do
a kötéseket, a tagdefiníciókat és minden mást, ami egy nem általános típusdefinícióban megengedett.
A többi szintaxiselem megegyezik a nem általános függvények és típusokéval. Az objektumazonosító például egy olyan azonosító, amely magát az objektumot jelöli.
A tulajdonságok, mezők és konstruktorok nem lehetnek általánosabbak a beágyazási típusnál. Emellett a modul értékei nem lehetnek általánosak.
Implicit módon általános szerkezetek
Amikor az F#-fordító a kódban szereplő típusokat következteti, automatikusan általánosként kezeli az általánosan használható függvényeket. Ha explicit módon ad meg egy típust, például paramétertípust, megakadályozza az automatikus általánosítást.
Az alábbi példakód általános, annak ellenére, makeList
hogy sem a paramétere, sem a paraméterei nem vannak explicit módon általánosként deklarálva.
let makeList a b =
[a; b]
A függvény aláírása a következő lesz 'a -> 'a -> 'a list
. Vegye figyelembe, hogy a
b
ebben a példában a rendszer arra következtet, hogy ugyanazzal a típussal rendelkezik. Ennek az az oka, hogy ezek együtt szerepelnek egy listában, és a lista összes elemének azonos típusúnak kell lennie.
Egy függvényt általánossá is tehet, ha egy típusjegyzetben az egyszeres idézőjel szintaxisával jelzi, hogy a paramétertípus általános típusparaméter. A következő kód általános, function1
mivel a paramétereket a rendszer így deklarálja típusparaméterekként.
let function1 (x: 'a) (y: 'a) =
printfn "%A %A" x y
Explicit módon általános szerkezetek
A függvényeket úgy is általánossá teheti, hogy a típusparamétereket szögletes zárójelekben (<type-parameter>
) explicit módon deklaráljuk. Az alábbi kód ezt mutatja be.
let function2<'T> (x: 'T) (y: 'T) =
printfn "%A, %A" x y
Általános szerkezetek használata
Általános függvények vagy metódusok használatakor előfordulhat, hogy nem kell megadnia a típusargumentumokat. A fordító típuskövetkeztetést használ a megfelelő típusargumentumok kikövetkeztetésére. Ha továbbra is kétértelműség áll fenn, a típusargumentumokat szögletes zárójelekben is megadhatja, és több típusargumentumot vesszővel választ el egymástól.
Az alábbi kód az előző szakaszokban definiált függvények használatát mutatja be.
// In this case, the type argument is inferred to be int.
function1 10 20
// In this case, the type argument is float.
function1 10.0 20.0
// Type arguments can be specified, but should only be specified
// if the type parameters are declared explicitly. If specified,
// they have an effect on type inference, so in this example,
// a and b are inferred to have type int.
let function3 a b =
// The compiler reports a warning:
function1<int> a b
// No warning.
function2<int> a b
Megjegyzés
Az általános típusra kétféleképpen hivatkozhat név szerint. Például list<int>
int list
két módon hivatkozhat egy általános típusra list
, amely egyetlen típusargumentumot int
használ. Az utóbbi formát hagyományosan csak beépített F# típusokkal használják, például list
és option
. Ha több típusargumentum is létezik, akkor általában a szintaxist Dictionary<int, string>
használja, de a szintaxist is használhatja (int, string) Dictionary
.
Helyettesítő karakterek típusargumentumként
Annak megadásához, hogy a fordítónak ki kell következtetnie egy típusargumentumra, használhatja az aláhúzásjelet vagy a helyettesítő karaktert (_
) a névvel ellátott típusargumentum helyett. Ez az alábbi kódban látható.
let printSequence (sequence1: Collections.seq<_>) =
Seq.iter (fun elem -> printf "%s " (elem.ToString())) sequence1
Általános típusok és függvények korlátozásai
Általános típus- vagy függvénydefiníciókban csak azokat a szerkezeteket használhatja, amelyekről ismert, hogy elérhetők az általános típusparaméteren. Ez a függvény- és metódushívások fordításkor történő ellenőrzésének engedélyezéséhez szükséges. Ha explicit módon deklarálja a típusparamétereket, explicit kényszert alkalmazhat egy általános típusparaméterre, hogy értesítse a fordítót arról, hogy bizonyos metódusok és függvények elérhetők. Ha azonban engedélyezi az F#-fordító számára az általános paramétertípusokból való következtetést, az határozza meg a megfelelő korlátozásokat. További információ: Korlátozások.
Statikusan feloldott típusparaméterek
Az F#-programokban kétféle típusparaméter használható. Az első az előző szakaszokban leírt típusú általános típusú paraméterek. Ez az első típusparaméter-típus egyenértékű az olyan nyelveken használt általános típusparaméterekkel, mint a Visual Basic és a C#. Egy másik típusparaméter az F#-ra jellemző, és statikusan feloldott típusparaméternek nevezzük. További információ ezekről a szerkezetekről: Statikusan feloldott típusparaméterek.
Példák
// A generic function.
// In this example, the generic type parameter 'a makes function3 generic.
let function3 (x : 'a) (y : 'a) =
printf "%A %A" x y
// A generic record, with the type parameter in angle brackets.
type GR<'a> =
{
Field1: 'a;
Field2: 'a;
}
// A generic class.
type C<'a>(a : 'a, b : 'a) =
let z = a
let y = b
member this.GenericMethod(x : 'a) =
printfn "%A %A %A" x y z
// A generic discriminated union.
type U<'a> =
| Choice1 of 'a
| Choice2 of 'a * 'a
type Test() =
// A generic member
member this.Function1<'a>(x, y) =
printfn "%A, %A" x y
// A generic abstract method.
abstract abstractMethod<'a, 'b> : 'a * 'b -> unit
override this.abstractMethod<'a, 'b>(x:'a, y:'b) =
printfn "%A, %A" x y