Apa aturan praktis Anda saat menggunakan struct vs kelas? Saya sedang memikirkan definisi C # dari istilah-istilah itu tetapi jika bahasa Anda memiliki konsep yang sama, saya ingin mendengar pendapat Anda juga.
Saya cenderung menggunakan kelas untuk hampir semua hal, dan menggunakan struct hanya ketika ada sesuatu yang sangat sederhana dan harus menjadi tipe nilai, seperti PhoneNumber atau sesuatu seperti itu. Tapi ini sepertinya penggunaan yang relatif kecil dan saya harap ada kasus penggunaan yang lebih menarik.
Jawaban:
Aturan umum yang harus diikuti adalah bahwa struct harus berupa kumpulan properti terkait yang kecil, sederhana (satu tingkat), yang tidak dapat diubah begitu dibuat; untuk hal lain, gunakan kelas.
C # bagus karena struct dan kelas tidak memiliki perbedaan eksplisit dalam deklarasi selain kata kunci yang menentukan; jadi, jika Anda merasa perlu "meningkatkan" sebuah struct ke suatu kelas, atau sebaliknya "menurunkan" sebuah kelas ke sebuah struct, itu sebagian besar masalah sederhana mengubah kata kunci (ada beberapa gotchas lainnya; struct tidak dapat memperoleh dari kelas atau tipe struct lainnya, dan mereka tidak dapat secara eksplisit mendefinisikan konstruktor tanpa parameter default).
Saya mengatakan "sebagian besar", karena hal yang lebih penting untuk diketahui tentang struct adalah bahwa, karena mereka adalah tipe nilai, memperlakukan mereka seperti kelas (tipe referensi) dapat berakhir dengan rasa sakit setengah. Khususnya, membuat sifat-sifat struktur bisa berubah bisa menyebabkan perilaku yang tidak terduga.
Misalnya, Anda memiliki kelas SimpleClass dengan dua properti, A dan B. Anda instantiate salinan kelas ini, menginisialisasi A dan B, dan kemudian meneruskan contoh ke metode lain. Metode itu selanjutnya memodifikasi A dan B. Kembali ke fungsi pemanggilan (yang membuat instance), A dan B instance Anda akan memiliki nilai yang diberikan kepada mereka oleh metode yang dipanggil.
Sekarang, Anda membuatnya menjadi sebuah struct. Properti masih bisa berubah. Anda melakukan operasi yang sama dengan sintaks yang sama seperti sebelumnya, tetapi sekarang, nilai-nilai baru A dan B tidak dalam contoh setelah memanggil metode. Apa yang terjadi? Nah, kelas Anda sekarang adalah sebuah struct, yang berarti itu adalah tipe nilai. Jika Anda meneruskan jenis nilai ke suatu metode, default (tanpa kata kunci keluar atau ref) adalah meneruskan "menurut nilai"; salinan dangkal instance dibuat untuk digunakan oleh metode, dan kemudian dihancurkan ketika metode dilakukan meninggalkan instance awal utuh.
Ini menjadi lebih membingungkan jika Anda memiliki tipe referensi sebagai anggota struct Anda (tidak dianulir, tetapi praktik yang sangat buruk dalam hampir semua kasus); kelas tidak akan dikloning (hanya referensi struct untuknya), jadi perubahan pada struct tidak akan memengaruhi objek asli, tetapi perubahan pada subkelas struct AKAN memengaruhi instance dari kode panggilan. Ini dapat dengan mudah menempatkan struct yang bisa berubah dalam kondisi yang sangat tidak konsisten yang dapat menyebabkan kesalahan jauh dari tempat masalah sebenarnya.
Untuk alasan ini, hampir setiap otoritas di C # mengatakan untuk selalu membuat struktur Anda tidak berubah; memungkinkan konsumen untuk menentukan nilai properti hanya pada konstruksi objek, dan tidak pernah menyediakan sarana apa pun untuk mengubah nilai instance itu. Bidang hanya baca, atau properti hanya dapatkan, adalah aturannya. Jika konsumen ingin mengubah nilainya, mereka dapat membuat objek baru berdasarkan nilai-nilai yang lama, dengan perubahan yang mereka inginkan, atau mereka dapat memanggil metode yang akan melakukan hal yang sama. Ini memaksa mereka untuk memperlakukan satu instance dari struct Anda sebagai satu "nilai" konseptual, tidak dapat dibagi dan berbeda dari (tetapi mungkin setara dengan) semua yang lain. Jika mereka melakukan operasi pada "nilai" yang disimpan oleh tipe Anda, mereka mendapatkan "nilai" baru yang berbeda dari nilai awal mereka,
Untuk contoh yang baik, lihat tipe DateTime. Anda tidak dapat menetapkan salah satu bidang instance DateTime secara langsung; Anda harus membuat yang baru, atau memanggil metode yang sudah ada yang akan menghasilkan contoh baru. Ini karena tanggal dan waktu adalah "nilai", seperti angka 5, dan perubahan ke angka 5 menghasilkan nilai baru yang bukan 5. Hanya karena 5 + 1 = 6 tidak berarti 5 sekarang 6 karena Anda menambahkan 1 ke dalamnya. DateTimes bekerja dengan cara yang sama; 12:00 tidak "menjadi" 12:01 jika Anda menambahkan satu menit, Anda malah mendapatkan nilai baru 12:01 yang berbeda dari 12:00. Jika ini adalah keadaan logis untuk jenis Anda (contoh konseptual yang bagus yang tidak ada di dalam. NET adalah Uang, Jarak, Berat, dan jumlah lain dari UOM di mana operasi harus memperhitungkan semua bagian dari nilai tersebut), kemudian gunakan struct dan desain yang sesuai. Dalam kebanyakan kasus lain di mana sub-item suatu objek harus bisa berubah secara independen, gunakan kelas.
sumber
Jawabannya: "Gunakan struct untuk konstruksi data murni, dan kelas untuk objek dengan operasi" jelas salah IMO. Jika struct memegang sejumlah besar properti maka kelas hampir selalu lebih tepat. Microsoft sering mengatakan, dari sudut pandang efisiensi, jika tipe Anda lebih besar dari 16 byte itu harus kelas.
https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes
sumber
Perbedaan paling penting antara a
class
dan astruct
adalah apa yang terjadi dalam situasi berikut:Apa yang seharusnya menjadi efek dari pernyataan terakhir
thing1.somePropertyOrField
? JikaThing
sebuah struct, dansomePropertyOrField
merupakan bidang publik yang terbuka, objekthing1
danthing2
akan "terlepas" dari satu sama lain, sehingga pernyataan terakhir tidak akan mempengaruhithing1
. JikaThing
kelas, makathing1
danthing2
akan dilampirkan satu sama lain, sehingga pernyataan terakhir akan menulisthing1.somePropertyOrField
. Seseorang harus menggunakan struct dalam kasus-kasus di mana semantik sebelumnya lebih masuk akal, dan harus menggunakan kelas dalam kasus-kasus di mana semantik terakhir akan lebih masuk akal.Perhatikan bahwa sementara beberapa orang menyarankan bahwa keinginan untuk membuat sesuatu yang bisa berubah adalah argumen yang mendukungnya menjadi kelas, saya akan menyarankan sebaliknya adalah benar: jika sesuatu yang ada untuk tujuan menyimpan beberapa data akan bisa berubah, dan jika tidak akan jelas apakah instance terpasang pada sesuatu, benda tersebut harus berupa struct (kemungkinan dengan bidang yang terbuka) sehingga memperjelas bahwa instance tidak melekat pada hal lain.
Misalnya, perhatikan pernyataan:
Akankah pernyataan kedua mengubah informasi yang disimpan
myPeople
? JikaPerson
adalah struct bidang-terbuka, itu tidak akan, dan fakta bahwa itu tidak akan menjadi konsekuensi yang jelas sebagai struktur-bidang terbuka ; jikaPerson
adalah struct dan seseorang ingin memperbaruimyPeople
, seseorang jelas harus melakukan sesuatu sepertimyPeople.UpdatePerson("123-45-6789", somePerson)
. JikaPerson
kelas, namun, mungkin jauh lebih sulit untuk menentukan apakah kode di atas tidak akan memperbarui isiMyPeople
, selalu memperbaruinya, atau kadang-kadang memperbaruinya.Berkenaan dengan gagasan bahwa struct harus "abadi", saya tidak setuju secara umum. Ada kasus penggunaan yang valid untuk struct "tidak dapat diubah" (di mana invarian diterapkan dalam sebuah konstruktor) tetapi mengharuskan seluruh struktur harus ditulis ulang kapan saja bagian mana pun dari perubahan itu adalah canggung, boros, dan lebih cenderung menyebabkan bug daripada sekadar mengekspos bug. bidang secara langsung. Misalnya, pertimbangkan
PhoneNumber
struct yang bidangnya, sertakan antara lain,AreaCode
danExchange
, dan anggaplah seseorang memiliki aList<PhoneNumber>
. Efek dari berikut ini harus cukup jelas:Perhatikan bahwa tidak ada dalam kode di atas yang tahu atau peduli tentang bidang apa pun
PhoneNumber
selainAreaCode
danExchange
. JikaPhoneNumber
apa yang disebut sebagai struct "tidak dapat diubah", akan diperlukan bahwa ia menyediakanwithXX
metode untuk setiap bidang, yang akan mengembalikan instance struct baru yang memegang nilai pass-in di bidang yang ditunjukkan, atau kalau tidak akan diperlukan untuk kode seperti di atas untuk mengetahui tentang setiap bidang dalam struct. Tidak persis menarik.BTW, setidaknya ada dua kasus di mana struct dapat memegang referensi untuk jenis yang bisa berubah.
Dalam skenario sebelumnya, IDENTITAS objek tidak akan berubah; di yang kedua, kelas objek bersarang mungkin tidak menegakkan imutabilitas, tetapi struct yang memegang referensi akan.
sumber
Saya cenderung menggunakan struct hanya ketika ada satu metode yang diperlukan sehingga membuat kelas tampak terlalu "berat". Jadi terkadang saya memperlakukan struct sebagai objek yang ringan. WASPADALAH: ini tidak selalu berhasil dan tidak selalu merupakan hal terbaik untuk dilakukan, jadi itu benar-benar tergantung pada situasinya!
SUMBER DAYA EKSTRA
Untuk penjelasan yang lebih formal, MSDN mengatakan: http://msdn.microsoft.com/en-us/library/ms229017.aspx Tautan ini memverifikasi apa yang saya sebutkan di atas, struct hanya boleh digunakan untuk menampung sejumlah kecil data.
sumber
Gunakan struct untuk konstruk data murni, dan kelas untuk objek dengan operasi.
Jika struktur data Anda tidak memerlukan kontrol akses dan tidak memiliki operasi khusus selain get / set, gunakan struct. Ini membuatnya jelas bahwa semua struktur itu adalah wadah untuk data.
Jika struktur Anda memiliki operasi yang memodifikasi struktur dengan cara yang lebih kompleks, gunakan kelas. Kelas juga dapat berinteraksi dengan kelas lain melalui argumen ke metode.
Anggap saja sebagai perbedaan antara batu bata dan pesawat terbang. Bata adalah sebuah struct; memiliki panjang, lebar dan tinggi. Itu tidak bisa berbuat banyak. Sebuah pesawat bisa memiliki beberapa operasi yang bermutasi, entah bagaimana, seperti memindahkan permukaan kontrol dan mengubah dorongan mesin. Akan lebih baik sebagai kelas.
sumber