Kelas (F#)

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

Sintaks

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

Keterangan

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

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

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

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

member-list terdiri dari konstruktor tambahan, deklarasi instans dan metode statis, deklarasi antarmuka, pengikatan abstrak, serta deklarasi properti dan peristiwa. Semua ini dijelaskan dalam Anggota.

identifier yang digunakan dengan kata kunci opsional as memberikan nama ke variabel instans, atau pengidentifikasi mandiri, 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 rekursif timbal balik, yang merupakan jenis yang saling mereferensikan satu sama lain, digabungkan dengan kata kunci and sama seperti fungsi yang saling rekursif. Misalnya, lihat bagian Jenis Rekursif Timbal Balik.

Konstruktor

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

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

new(argument-list) = constructor-body

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

Contoh berikut menggambarkan 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)

Pengikatan let and do

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

Bidang yang dibuat oleh pengikatan let 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. Bidang tersebut tidak dapat diakses dengan menggunakan pengidentifikasi mandiri, jika ada.

Pengidentifikasi Mandiri

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

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

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

Contoh kode berikut mengilustrasikan dua cara untuk membuat pengidentifikasi mandiri. Di baris pertama, kata kunci as digunakan untuk menentukan pengidentifikasi mandiri. Di baris kelima, pengidentifikasi this digunakan untuk menentukan pengidentifikasi mandiri 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 pemrogram .NET lainnya, Anda dapat memberi nama pengidentifikasi mandiri sesuai keinginan Anda; Anda tidak dibatasi untuk menamainya seperti self, Me, atau this.

Pengidentifikasi mandiri yang dinyatakan dengan kata kunci as 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 bahasa umum. Anda dapat menggunakan pengidentifikasi mandiri secara bebas setelah konstruktor dasar, seperti dalam pengikatan let atau pengikatan do.

Parameter Jenis Generik

Parameter jenis generik ditentukan dalam tanda kurung sudut (< dan >), dalam bentuk tanda kutip tunggal diikuti oleh sebuah pengidentifikasi. Beberapa parameter jenis generik dipisahkan oleh koma. Parameter jenis generik berada dalam cakupan di seluruh 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 tupel.

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

Menentukan Pewarisan

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 bahasa pemrogram base 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. Pengikatan let and do tidak dapat muncul di bagian ini. Karena anggota dapat ditambahkan ke berbagai jenis F# selain ke kelas, semua ini dibahas dalam topik terpisah, Anggota.

Jenis Rekursif Timbal Balik

Saat Anda menentukan jenis yang saling mereferensikan dengan cara melingkar, Anda meringkas definisi jenis dengan menggunakan kata kunci and. Kata kunci and menggantikan kata kunci type pada semua kecuali definisi pertama, seperti 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, Gabungan, Rekaman, dan Struktur

Mengingat berbagai jenis pilihan, Anda harus memiliki pemahaman yang baik tentang apa yang dirancang untuk setiap jenis untuk memilih jenis yang sesuai untuk situasi tertentu. Kelas didesain untuk penggunaan dalam konteks pemrograman berorientasi objek. Pemrograman berorientasi objek merupakan paradigma dominan yang digunakan dalam aplikasi yang ditulis untuk .NET Framework. Jika kode F# Anda harus bekerja sama erat 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 berinteroperasi 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 gabungan yang diskriminasi. Satu gabungan yang diskriminasi yang dipikirkan dengan baik, bersama dengan kode pencocokan pola yang sesuai, sering kali dapat digunakan sebagai alternatif yang lebih sederhana untuk hierarki objek. Untuk informasi tentang gabungan yang terdiskriminasi, lihat Gabungan Terdiskriminasi.

Rekaman memiliki keuntungan lebih sederhana daripada kelas, tetapi rekaman tidak sesuai ketika tuntutan jenis melebihi apa yang dapat dicapai dengan kesederhanaannya. 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 perilakunya lebih kompleks, bidang yang disimpan dalam rekaman masih merupakan agregat nilai sederhana. Untuk informasi selengkapnya tentang rekaman, lihat Rekaman.

Struktur juga berguna untuk agregat kecil data, tetapi berbeda dari kelas dan rekaman karena struktur merupakan jenis nilai .NET. Kelas dan rekaman merupakan jenis referensi .NET. Semantik jenis nilai dan jenis referensi berbeda dalam jenis nilai tersebut yang diteruskan oleh nilai. Ini berarti bahwa mereka disalin bit 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 timbunan. Oleh karena itu, struktur sesuai untuk data yang sering diakses ketika overhead pengaksesan timbunan menjadi masalah. Untuk informasi selengkapnya tentang struktur, lihat Struct.

Lihat juga