Bagikan melalui


Ekstensi jenis

Ekstensi jenis (juga disebut augmentasi) adalah keluarga fitur yang memungkinkan Anda menambahkan anggota baru ke jenis objek yang ditentukan sebelumnya. 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.

Sintaksis

// 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 didefinisikan dalam file yang sama dan di namespace atau modul yang sama dengan jenis yang diperluas. Definisi lain akan mengakibatkan 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 masing-masing hal berikut:

  • Deklarasi jenis Variant
  • Fungsionalitas untuk mencetak Variant kelas tergantung pada "bentuk"
  • Cara untuk mengakses fungsionalitas pencetakan dengan object-style .-notation

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

Ekstensi jenis intrinsik dikompilasi sebagai anggota dari jenis penambahannya, dan muncul pada jenis ketika jenis diperiksa oleh pantulan.

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. Contohnya:

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 Extensions modul dibuka dalam cakupan tempat Anda bekerja.

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

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

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

Batasan generik ekstensi jenis intrinsik dan opsional

Dimungkinkan untuk mendeklarasikan ekstensi jenis pada jenis generik tempat variabel jenis dibatasi. Persyaratannya adalah bahwa batasan deklarasi ekstensi cocok dengan batasan jenis yang dinyatakan.

Namun, bahkan ketika batasan dicocokkan antara jenis yang dinyatakan dan ekstensi jenis, ada kemungkinan batasan disimpulkan oleh isi anggota yang diperluas yang memberlakukan persyaratan yang berbeda pada parameter jenis daripada jenis yang dinyatakan. Contohnya:

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 mendapatkan kode ini untuk bekerja dengan ekstensi jenis opsional:

  • Apa adanya Sum , anggota 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.

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

Metode ekstensi

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

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

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 didefinisikan pada IEnumerable<T>, selama Extensions telah dibuka atau berada dalam cakupan.

Agar ekstensi tersedia untuk VB.NET kode, 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 dapat diperpanjang.
  • Ekstensi jenis intrinsik dan opsional dapat menentukan jenis anggota apa pun , 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 statis atau instans.
  • Jenis variabel 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 penimpaan sebagai augmentasi.
  • Ekstensi jenis tidak mendukung Parameter Jenis yang Diselesaikan Secara Statis.
  • Ekstensi Jenis Opsional tidak mendukung konstruktor sebagai augmentasi.
  • Ekstensi jenis tidak dapat didefinisikan pada singkatan jenis.
  • Ekstensi jenis tidak valid untuk byref<'T> (meskipun dapat dinyatakan).
  • Ekstensi jenis tidak valid untuk atribut (meskipun dapat dideklarasikan).
  • 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 beberapa ekstensi jenis intrinsik ada untuk satu jenis, semua anggota harus unik. Untuk ekstensi jenis opsional, anggota dalam ekstensi jenis yang berbeda ke 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