Ekstensi jenis

Ekstensi jenis (juga disebut augmentasi) adalah keluarga fitur yang memungkinkan Anda menambahkan anggota baru ke jenis objek yang telah ditentukan. Ketiga fitur tersebut adalah:

  • Ekstensi jenis intrinsik
  • Ekstensi jenis opsional
  • Metode ekstensi

Masing-masing dapat digunakan dalam skenario yang berbeda dan memiliki tradeoff yang berbeda.

Sintaks

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

Ekstensi jenis intrinsik

Ekstensi jenis intrinsik adalah ekstensi jenis yang memperluas jenis yang ditentukan pengguna.

Ekstensi jenis intrinsik harus ditentukan dalam file yang sama dan di namespace atau modul yang sama dengan jenis yang diperluas. Definisi lain akan membuat mereka menjadi ekstensi jenis opsional.

Ekstensi jenis intrinsik terkadang merupakan cara yang lebih bersih untuk memisahkan fungsionalitas dari deklarasi jenis. Contoh berikut menunjukkan cara menentukan ekstensi jenis intrinsik:

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

Menggunakan ekstensi jenis memungkinkan Anda memisahkan hal berikut:

  • Deklarasi jenis Variant
  • Fungsionalitas untuk mencetak kelas Variant tergantung pada "shape (bentuk)"
  • Cara untuk mengakses fungsionalitas pencetakan dengan .-notation gaya objek

Ini adalah alternatif untuk menentukan semuanya sebagai anggota di Variant. Meski bukan pendekatan yang lebih baik secara inheren, ini bisa menjadi representasi fungsionalitas yang lebih bersih di beberapa situasi.

Ekstensi jenis intrinsik dikompilasi sebagai anggota dari jenis yang diaugmentasi, dan muncul pada jenis ketika jenis diperiksa oleh refleksi.

Ekstensi jenis opsional

Ekstensi jenis opsional adalah ekstensi yang muncul di luar modul asli, namespace, atau perakitan jenis yang diperluas.

Ekstensi jenis opsional berguna untuk memperluas jenis yang belum Anda tentukan sendiri. Misalnya:

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
        }

Anda sekarang dapat mengakses RepeatElements seolah-olah itu adalah anggota IEnumerable<T> selama modul Extensions dibuka dalam cakupan hal yang sedang Anda kerjakan.

Ekstensi opsional tidak muncul pada jenis yang diperluas saat diperiksa oleh refleksi. Ekstensi opsional harus berada dalam modul, dan hanya berada dalam cakupan ketika modul yang berisi ekstensi terbuka atau berada dalam cakupan.

Anggota ekstensi opsional dikompilasi ke anggota statik yang instans objeknya diteruskan secara implisit sebagai parameter pertama. Namun, mereka bertindak seolah-olah mereka adalah anggota instans atau anggota statik sesuai dengan cara mereka dinyatakan.

Anggota ekstensi opsional juga tidak terlihat oleh konsumen C# atau Visual Basic. Mereka hanya dapat digunakan dalam kode F# lainnya.

Batasan generik ekstensi jenis intrinsik dan opsional

Anda dapat mendeklarasikan ekstensi jenis pada jenis generik di mana variabel jenis dibatasi. Persyaratannya adalah batasan deklarasi ekstensi cocok dengan batasan jenis yang dinyatakan.

Namun, bahkan ketika batasan cocok antara jenis yang dinyatakan dan ekstensi jenis, batasan mungkin disimpulkan oleh isi anggota yang diperluas yang memberlakukan persyaratan berbeda pada parameter jenis dibandingkan jenis yang dinyatakan. Misalnya:

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

Tidak ada cara untuk membuat kode ini bekerja dengan ekstensi jenis opsional:

  • Anggota Sum memiliki batasan yang berbeda pada 'T (static member get_Zero dan static member (+)) dari apa yang ditentukan ekstensi jenis.
  • Memodifikasi ekstensi jenis agar memiliki batasan yang sama seperti Sum tidak akan lagi cocok dengan batasan yang ditentukan pada IEnumerable<'T>.
  • Mengubah member this.Sum menjadi member inline this.Sum akan memberikan kesalahan bahwa batasan jenis tidak cocok.

Hal yang diinginkan adalah metode statik yang "mengapung di ruang" dan dapat disajikan seolah-olah mereka memperluas jenis. Di sinilah metode ekstensi diperlukan.

Metode ekstensi

Terakhir, metode ekstensi (terkadang disebut "anggota ekstensi gaya C#") dapat dideklarasikan dalam F# sebagai metode anggota statik pada kelas.

Metode ekstensi berguna ketika Anda ingin menentukan ekstensi pada jenis generik yang akan membatasi variabel jenis. Misalnya:

namespace Extensions

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

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

Ketika digunakan, kode ini akan membuatnya muncul seolah-olah Sum ditentukan di IEnumerable<T>, selama Extensions telah dibuka atau berada dalam cakupan.

Agar ekstensi tersedia untuk kode VB.NET, diperlukan tambahan ExtensionAttribute pada tingkat perakitan:

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

Keterangan lainnya

Ekstensi jenis juga memiliki atribut berikut:

  • Jenis apa pun yang dapat diakses tentunya dapat diperluas.
  • Ekstensi jenis intrinsik dan opsional dapat menentukan semua jenis anggota, bukan hanya metode. Jadi, properti ekstensi juga dimungkinkan, misalnya.
  • Token self-identifier dalam sintaks mewakili instans jenis yang dipanggil, sama seperti anggota biasa.
  • Anggota yang diperluas dapat menjadi anggota statik atau instans.
  • Variabel jenis pada ekstensi jenis harus cocok dengan batasan jenis yang dideklarasikan.

Batasan berikut juga ada untuk ekstensi jenis:

  • Ekstensi jenis tidak mendukung metode virtual atau abstrak.
  • Ekstensi jenis tidak mendukung metode pengambilalihan sebagai augmentasi.
  • Ekstensi janis tidak mendukung Parameter Jenis yang Diselesaikan Secara Statik.
  • Ekstensi Jenis Opsional tidak mendukung konstruktor sebagai augmentasi.
  • Ekstensi jenis tidak dapat ditentukan di singkatan jenis.
  • Ekstensi jenis tidak valid untuk byref<'T> (meski dapat dinyatakan).
  • Ekstensi jenis tidak valid untuk atribut (meski dapat dinyatakan).
  • Anda dapat menentukan ekstensi yang membebani metode lain dengan nama yang sama, tetapi pengompilasi F# memberikan preferensi pada metode non-ekstensi jika ada panggilan ambigu.

Terakhir, jika ada beberapa ekstensi jenis intrinsik untuk satu jenis, semua anggota haruslah unik. Untuk ekstensi jenis opsional, anggota dalam ekstensi jenis yang berbeda hingga jenis yang sama dapat memiliki nama yang sama. Kesalahan ambiguitas hanya terjadi jika kode klien membuka dua cakupan berbeda yang menentukan nama anggota yang sama.

Lihat juga