Bagikan melalui


Kelas (F#)

Kelas adalah jenis yang mewakili objek yang dapat memiliki properti, metode, dan peristiwa.

Sintaksis

// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
[ class ]
[ inherit base-type-name(base-constructor-args) ]
[ let-bindings ]
[ do-bindings ]
member-list
...
[ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...

Komentar

Kelas mewakili deskripsi mendasar dari jenis objek .NET; kelas adalah konsep jenis utama yang mendukung pemrograman berorientasi objek di F#.

Dalam sintaks sebelumnya, type-name adalah pengidentifikasi yang valid. menjelaskan type-params parameter jenis generik opsional. Ini terdiri dari nama parameter jenis dan batasan yang diapit dalam tanda kurung sudut (< dan >). Untuk informasi selengkapnya, lihat Generik dan Batasan. menjelaskan parameter-list parameter konstruktor. Pengubah akses pertama berkaitan dengan jenis; kedua berkaitan dengan konstruktor utama. Dalam kedua kasus, defaultnya adalah public.

Anda menentukan kelas dasar untuk kelas dengan menggunakan inherit kata kunci. Anda harus menyediakan argumen, dalam tanda kurung, untuk konstruktor kelas dasar.

Anda mendeklarasikan bidang atau nilai fungsi yang lokal ke kelas dengan menggunakan let pengikatan, dan Anda harus mengikuti aturan umum untuk let pengikatan. Bagian ini do-bindings mencakup kode yang akan dijalankan setelah konstruksi objek.

member-list Terdiri dari konstruktor tambahan, deklarasi instans dan metode statis, deklarasi antarmuka, pengikatan abstrak, dan deklarasi properti dan peristiwa. Ini dijelaskan dalam Anggota.

identifier Yang digunakan dengan kata kunci opsional as memberikan nama ke variabel instans, atau pengidentifikasi diri, yang dapat digunakan dalam definisi jenis untuk merujuk ke instans jenis. Untuk informasi selengkapnya, lihat bagian Pengidentifikasi Mandiri nanti dalam topik ini.

Kata kunci class dan end yang menandai awal dan akhir definisi bersifat opsional.

Jenis yang saling rekursif, yang merupakan jenis yang saling mereferensikan, digabungkan dengan and kata kunci sama seperti fungsi yang saling rekursif. Misalnya, lihat bagian Jenis Rekursif Bersama.

Konstruktor

Konstruktor adalah kode yang membuat instans jenis kelas. Konstruktor untuk kelas bekerja agak berbeda dalam F# daripada yang mereka lakukan dalam bahasa .NET lainnya. Dalam kelas F#, selalu ada konstruktor utama yang argumennya dijelaskan dalam parameter-list yang mengikuti nama jenis, dan yang isinya terdiri dari let (dan let rec) pengikatan pada awal deklarasi kelas dan do pengikatan yang mengikuti. Argumen konstruktor utama berada dalam cakupan sepanjang deklarasi kelas.

Anda dapat menambahkan konstruktor tambahan dengan menggunakan new kata kunci untuk menambahkan anggota, sebagai berikut:

new(argument-list) = constructor-body

Isi konstruktor baru harus memanggil konstruktor utama yang ditentukan di bagian atas deklarasi kelas.

Contoh berikut mengilustrasikan konsep ini. Dalam kode berikut, MyClass memiliki dua konstruktor, konstruktor utama yang mengambil dua argumen dan konstruktor lain yang tidak mengambil argumen.

type MyClass1(x: int, y: int) =
    do printfn "%d %d" x y
    new() = MyClass1(0, 0)

biarkan dan lakukan Pengikatan

Pengikatan let dan do dalam definisi kelas membentuk isi konstruktor kelas utama, dan oleh karena itu mereka berjalan setiap kali instans kelas dibuat. Jika pengikatan let adalah fungsi, maka pengikatan akan dikompilasi menjadi anggota. Jika pengikatan let adalah nilai yang tidak digunakan dalam fungsi atau anggota apa pun, maka pengikatan tersebut dikompilasi ke dalam variabel yang bersifat lokal untuk konstruktor. Jika tidak, itu dikompilasi ke dalam bidang kelas. do Ekspresi berikut dikompilasi ke dalam konstruktor utama dan menjalankan kode inisialisasi untuk setiap instans. Karena setiap konstruktor tambahan selalu memanggil konstruktor utama, let pengikatan dan do pengikatan selalu dijalankan terlepas dari konstruktor mana yang dipanggil.

Bidang yang dibuat oleh let pengikatan dapat diakses di seluruh metode dan properti kelas; namun, bidang tersebut tidak dapat diakses dari metode statis, bahkan jika metode statis mengambil variabel instans sebagai parameter. Mereka tidak dapat diakses dengan menggunakan pengidentifikasi mandiri, jika ada.

Pengidentifikasi Mandiri

Pengidentifikasi mandiri adalah nama yang mewakili instans saat ini. Pengidentifikasi mandiri menyerupai this kata kunci di C# atau C++ atau Me di Visual Basic. Anda dapat menentukan pengidentifikasi diri dengan dua cara berbeda, tergantung pada apakah Anda ingin pengidentifikasi diri berada dalam cakupan untuk seluruh definisi kelas atau hanya untuk metode individual.

Untuk menentukan pengidentifikasi mandiri untuk seluruh kelas, gunakan as kata kunci setelah tanda kurung penutup daftar parameter konstruktor, dan tentukan nama pengidentifikasi.

Untuk menentukan pengidentifikasi diri hanya untuk satu metode, berikan pengidentifikasi diri dalam deklarasi anggota, tepat sebelum nama metode dan titik (.) sebagai pemisah.

Contoh kode berikut mengilustrasikan dua cara untuk membuat pengidentifikasi diri. Di baris pertama, as kata kunci digunakan untuk menentukan pengidentifikasi diri. Di baris kelima, pengidentifikasi this digunakan untuk menentukan pengidentifikasi diri yang cakupannya dibatasi untuk metode PrintMessage.

type MyClass2(dataIn) as self =
    let data = dataIn
    do
        self.PrintMessage()
    member this.PrintMessage() =
        printf "Creating MyClass2 with Data %d" data

Tidak seperti dalam bahasa .NET lainnya, Anda dapat memberi nama pengidentifikasi diri sesuai keinginan Anda; Anda tidak dibatasi untuk nama seperti self, , Meatau this.

Pengidentifikasi diri yang dinyatakan dengan as kata kunci tidak diinisialisasi sampai setelah konstruktor dasar. Oleh karena itu, ketika digunakan sebelum atau di dalam konstruktor dasar, System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized. akan dinaikkan selama runtime. Anda dapat menggunakan pengidentifikasi mandiri dengan bebas setelah konstruktor dasar, seperti dalam let pengikatan atau do pengikatan.

Parameter Jenis Generik

Parameter jenis generik ditentukan dalam tanda kurung sudut (< dan >), dalam bentuk tanda kutip tunggal diikuti oleh pengidentifikasi. Beberapa parameter jenis generik dipisahkan oleh koma. Parameter jenis generik berada dalam cakupan sepanjang deklarasi. Contoh kode berikut menunjukkan cara menentukan parameter jenis generik.

type MyGenericClass<'a>(x: 'a) =
    do printfn "%A" x

Argumen jenis disimpulkan ketika jenis digunakan. Dalam kode berikut, jenis yang disimpulkan adalah urutan tuple.

let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })

Menentukan Warisan

Klausa inherit mengidentifikasi kelas dasar langsung, jika ada. Di F#, hanya satu kelas dasar langsung yang diizinkan. Antarmuka yang diterapkan kelas tidak dianggap sebagai kelas dasar. Antarmuka dibahas dalam topik Antarmuka .

Anda dapat mengakses metode dan properti kelas dasar dari kelas turunan dengan menggunakan kata kunci base bahasa sebagai pengidentifikasi, diikuti dengan titik (.) dan nama anggota.

Untuk informasi selengkapnya, lihat Warisan.

Bagian Anggota

Anda dapat menentukan metode statis atau instans, properti, implementasi antarmuka, anggota abstrak, deklarasi peristiwa, dan konstruktor tambahan di bagian ini. Biarkan dan lakukan pengikatan tidak dapat muncul di bagian ini. Karena anggota dapat ditambahkan ke berbagai jenis F# selain kelas, mereka dibahas dalam topik terpisah, Anggota.

Jenis Rekursif Bersama

Saat Anda menentukan jenis yang mereferensikan satu sama lain dengan cara melingkar, Anda meringkas definisi jenis dengan menggunakan and kata kunci. Kata and kunci menggantikan type kata kunci pada semua kecuali definisi pertama, sebagai berikut.

open System.IO

type Folder(pathIn: string) =
    let path = pathIn
    let filenameArray: string array = Directory.GetFiles(path)
    member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray

and File(filename: string, containingFolder: Folder) =
    member this.Name = filename
    member this.ContainingFolder = containingFolder

let folder1 = new Folder(".")

for file in folder1.FileArray do
    printfn "%s" file.Name

Output adalah daftar semua file di direktori saat ini.

Kapan Menggunakan Kelas, Serikat, Rekaman, dan Struktur

Mengingat berbagai jenis untuk dipilih, Anda harus memiliki pemahaman yang baik tentang apa yang dirancang untuk setiap jenis untuk memilih jenis yang sesuai untuk situasi tertentu. Kelas dirancang untuk digunakan dalam konteks pemrograman berorientasi objek. Pemrograman berorientasi objek adalah paradigma dominan yang digunakan dalam aplikasi yang ditulis untuk .NET Framework. Jika kode F# Anda harus bekerja sama dengan .NET Framework atau pustaka berorientasi objek lainnya, dan terutama jika Anda harus memperluas dari sistem jenis berorientasi objek seperti pustaka UI, kelas mungkin sesuai.

Jika Anda tidak beroperasi erat dengan kode berorientasi objek, atau jika Anda menulis kode yang mandiri dan karenanya dilindungi dari interaksi yang sering dengan kode berorientasi objek, Anda harus mempertimbangkan untuk menggunakan campuran kelas, rekaman, dan serikat yang diskriminasi. Satu penyatuan diskriminasi yang dipikirkan dengan baik, bersama dengan kode pencocokan pola yang sesuai, sering digunakan sebagai alternatif yang lebih sederhana untuk hierarki objek. Untuk informasi selengkapnya tentang serikat pekerja yang didiskriminasi, lihat Serikat Pekerja Diskriminasi.

Rekaman memiliki keuntungan lebih sederhana daripada kelas, tetapi catatan tidak sesuai ketika tuntutan jenis melebihi apa yang dapat dicapai dengan kesederhanaan mereka. Rekaman pada dasarnya adalah agregat nilai sederhana, tanpa konstruktor terpisah yang dapat melakukan tindakan kustom, tanpa bidang tersembunyi, dan tanpa pewarisan atau implementasi antarmuka. Meskipun anggota seperti properti dan metode dapat ditambahkan ke rekaman untuk membuat perilaku mereka lebih kompleks, bidang yang disimpan dalam rekaman masih merupakan agregat nilai sederhana. Untuk informasi selengkapnya tentang rekaman, lihat Rekaman.

Struktur juga berguna untuk agregat data kecil, tetapi berbeda dari kelas dan rekaman karena mereka adalah jenis nilai .NET. Kelas dan rekaman adalah jenis referensi .NET. Semantik jenis nilai dan jenis referensi berbeda dalam jenis nilai tersebut diteruskan oleh nilai. Ini berarti bahwa mereka disalin sedikit untuk bit ketika mereka diteruskan sebagai parameter atau dikembalikan dari fungsi. Mereka juga disimpan di tumpukan atau, jika digunakan sebagai bidang, disematkan di dalam objek induk alih-alih disimpan di lokasi terpisah mereka sendiri pada tumpukan. Oleh karena itu, struktur sesuai untuk data yang sering diakses ketika overhead mengakses timbunan adalah masalah. Untuk informasi selengkapnya tentang struktur, lihat Struktur.

Lihat juga