Megosztás a következőn keresztül:


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 és static 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ényszernek IEnumerable<'T>.
  • member inline this.Sum A módosítás member 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.

Lásd még