Setelah diskusi di SO, saya sudah membaca beberapa kali komentar bahwa struct yang bisa berubah adalah "jahat" (seperti dalam jawaban untuk pertanyaan ini ).
Apa masalah aktual dengan mutabilitas dan struct di C #?
c#
struct
immutability
mutable
Dirk Vollmar
sumber
sumber
int
,bool
s, dan semua tipe nilai lainnya adalah jahat. Ada kasus-kasus untuk berubah-ubah dan untuk tidak berubah. Kasus-kasus tersebut bergantung pada peran yang dimainkan data, bukan jenis alokasi / pembagian memori.int
danbool
yang tidak bisa berubah ...
-syntax, membuat operasi dengan data yang diketik ulang dan data yang diketik nilai terlihat sama meskipun mereka berbeda nyata. Ini adalah kesalahan properti C #, bukan struct — beberapa bahasa menawarkana[V][X] = 3.14
sintaks alternatif untuk bermutasi di tempat. Dalam C #, Anda sebaiknya menawarkan metode mutator struct-member seperti 'MutateV (Action <ref Vector2> mutator) `dan menggunakannya sepertia.MutateV((v) => { v.X = 3; })
(contohnya terlalu disederhanakan karena keterbatasan C # mengenairef
kata kunci, tetapi dengan beberapa solusi harus dimungkinkan) .x
) operasi tunggal ini adalah 4 instruksi:ldloc.0
(memuat variabel 0-indeks ke ...T
adalah tipe. Ref hanyalah kata kunci yang membuat variabel diteruskan ke metode itu sendiri, bukan salinannya. Ini juga masuk akal untuk jenis referensi, karena kita dapat mengubah variabel , yaitu referensi di luar metode akan menunjuk ke objek lain setelah diubah dalam metode. Karenaref T
bukan tipe, tetapi mode meneruskan parameter metode, Anda tidak bisa memasukkannya ke dalam<>
, karena hanya tipe yang bisa diletakkan di sana. Jadi itu tidak benar. Mungkin akan lebih mudah untuk melakukannya, mungkin tim C # dapat membuat ini untuk beberapa versi baru, tetapi sekarang mereka sedang mengerjakan beberapa ...Jawaban:
Structs adalah tipe nilai yang berarti mereka disalin ketika mereka diedarkan.
Jadi, jika Anda mengubah salinan Anda hanya mengubah salinan itu, bukan yang asli dan tidak ada salinan lain yang mungkin ada.
Jika struct Anda tidak dapat diubah maka semua salinan otomatis yang dihasilkan dari diteruskan oleh nilai akan sama.
Jika Anda ingin mengubahnya, Anda harus melakukannya secara sadar dengan membuat instance baru dari struct dengan data yang dimodifikasi. (bukan salinan)
sumber
Mulai dari mana ;-p
Blog Eric Lippert selalu baik untuk kutipan:
Pertama, Anda cenderung kehilangan perubahan dengan mudah ... misalnya, mengeluarkan beberapa hal dari daftar:
apa yang berubah? Tidak ada yang berguna ...
Sama dengan properti:
memaksa Anda untuk melakukan:
kurang kritis, ada masalah ukuran; objek yang bisa berubah cenderung memiliki banyak properti; namun jika Anda memiliki struct dengan dua
int
s, astring
, aDateTime
dan abool
, Anda dapat dengan cepat membakar banyak memori. Dengan kelas, beberapa penelepon dapat membagikan referensi ke instance yang sama (referensi kecil).sumber
++
operator. Dalam hal ini, kompiler hanya menulis tugas eksplisit itu sendiri daripada bergegas programmer.Saya tidak akan mengatakan yang jahat, tetapi bisa berubah-ubah sering kali merupakan pertanda kerinduan para programmer untuk memberikan fungsionalitas yang maksimal. Pada kenyataannya, ini seringkali tidak diperlukan dan pada gilirannya, membuat antarmuka lebih kecil, lebih mudah digunakan dan lebih sulit untuk menggunakan yang salah (= lebih kuat).
Salah satu contohnya adalah konflik baca / tulis dan tulis / tulis dalam kondisi lomba. Ini tidak dapat terjadi dalam struktur yang tidak dapat diubah, karena penulisan bukanlah operasi yang valid.
Juga, saya mengklaim bahwa mutabilitas hampir tidak pernah benar-benar diperlukan , programmer hanya berpikir bahwa mungkin di masa depan. Misalnya, tidak masuk akal untuk mengubah tanggal. Sebaliknya, buat tanggal baru berdasarkan tanggal yang lama. Ini adalah operasi yang murah, jadi kinerja bukan pertimbangan.
sumber
float
komponen transformasi grafik. Jika metode seperti itu mengembalikan struct bidang terbuka dengan enam komponen, jelas bahwa memodifikasi bidang struct tidak akan mengubah objek grafis dari mana ia diterima. Jika metode seperti itu mengembalikan objek kelas yang bisa berubah, mungkin mengubah propertinya akan mengubah objek grafis yang mendasarinya dan mungkin tidak - tidak ada yang tahu.Struct yang bisa berubah tidak jahat.
Mereka mutlak diperlukan dalam keadaan kinerja tinggi. Misalnya ketika garis cache dan atau pengumpulan sampah menjadi hambatan.
Saya tidak akan menyebut penggunaan struct tidak berubah dalam kasus-kasus penggunaan yang benar-benar valid ini "jahat".
Saya dapat melihat poin bahwa sintaksis C # tidak membantu membedakan akses anggota dari tipe nilai atau tipe referensi, jadi saya semua lebih memilih struct yang tidak dapat diubah, yang menegakkan immutabilitas, lebih dari struct yang bisa berubah.
Namun, alih-alih hanya memberi label struct yang tidak dapat diubah sebagai "jahat", saya akan menyarankan untuk merangkul bahasa dan menganjurkan aturan praktis yang lebih bermanfaat dan konstruktif.
Misalnya: "struct adalah tipe nilai, yang disalin secara default. Anda perlu referensi jika Anda tidak ingin menyalinnya" atau "coba bekerja dengan struct yang hanya dibaca dulu" .
sumber
struct
dengan bidang publik) daripada untuk mendefinisikan kelas yang dapat digunakan, dengan canggung, untuk mencapai tujuan yang sama, atau untuk menambahkan sekelompok sampah ke struct untuk membuatnya meniru kelas seperti itu (daripada memilikinya berperilaku seperti seperangkat variabel terjebak bersama dengan lakban, yang adalah apa yang sebenarnya diinginkan di tempat pertama)Bangunan dengan bidang atau properti yang bisa berubah publik tidak jahat.
Metode Struct (berbeda dari setter properti) yang bermutasi "ini" agak jahat, hanya karena .net tidak menyediakan cara untuk membedakan mereka dari metode yang tidak. Metode Struct yang tidak bermutasi "ini" harus tidak dapat dimasuki bahkan pada struct read-only tanpa perlu menyalin defensif. Metode yang bermutasi "ini" tidak boleh tidak bisa disangkal sama sekali pada struct read-only. Karena .net tidak ingin melarang metode struct yang tidak mengubah "ini" agar tidak dipanggil pada struct read-only, tetapi tidak ingin memperbolehkan struct read-only dimutasi, defensif menyalin struct di read- hanya konteks, bisa dibilang mendapatkan yang terburuk dari kedua dunia.
Meskipun ada masalah dengan penanganan metode yang bermutasi sendiri dalam konteks baca-saja, namun, struktur yang dapat berubah sering menawarkan semantik yang jauh lebih unggul daripada jenis kelas yang dapat berubah. Pertimbangkan tiga tanda tangan metode berikut:
Untuk setiap metode, jawab pertanyaan berikut:
Jawaban:
Method1 tidak dapat memodifikasi foo, dan tidak pernah mendapatkan referensi. Method2 mendapatkan referensi berumur pendek ke foo, yang dapat digunakan memodifikasi bidang foo beberapa kali, dalam urutan apa pun, sampai kembali, tetapi tidak dapat bertahan referensi itu. Sebelum Method2 kembali, kecuali ia menggunakan kode yang tidak aman, setiap dan semua salinan yang mungkin telah dibuat dari referensi 'foo' akan hilang. Method3, tidak seperti Method2, mendapatkan referensi foo yang bisa dibagi-bagi dengan foo, dan tidak ada yang tahu apa yang mungkin dilakukan dengan itu. Itu mungkin tidak mengubah foo sama sekali, mungkin mengubah foo dan kemudian kembali, atau mungkin memberikan referensi ke foo ke utas lain yang mungkin memutasinya dengan cara yang sewenang-wenang di waktu mendatang yang sewenang-wenang.
Susunan struktur menawarkan semantik yang indah. Diberikan RectArray [500] dari tipe Rectangle, jelas dan jelas bagaimana cara menyalin elemen 123 ke elemen 456 dan kemudian beberapa saat kemudian mengatur lebar elemen 123 hingga 555, tanpa elemen 456. "RectArray [432] = RectArray [321 ]; ...; RectArray [123] .Lebar = 555; ". Mengetahui bahwa Rectangle adalah sebuah struct dengan bidang bilangan bulat yang disebut Width akan memberi tahu semua orang yang perlu tahu tentang pernyataan di atas.
Sekarang misalkan RectClass adalah kelas dengan bidang yang sama dengan Rectangle dan seseorang ingin melakukan operasi yang sama pada RectClassArray [500] dari jenis RectClass. Mungkin array seharusnya menampung 500 referensi yang tidak dapat diinisialisasi untuk objek RectClass yang bisa diubah. dalam kasus itu, kode yang tepat akan menjadi sesuatu seperti "RectClassArray [321] .SetBounds (RectClassArray [456]); ...; RectClassArray [321] .X = 555;". Mungkin array diasumsikan memiliki instance yang tidak akan berubah, sehingga kode yang tepat akan lebih seperti "RectClassArray [321] = RectClassArray [456]; ...; RectClassArray [321] = New RectClass (RectClassArray [321] ]); RectClassArray [321] .X = 555; " Untuk mengetahui apa yang seharusnya dilakukan, kita harus tahu lebih banyak tentang RectClass (mis. Apakah itu mendukung copy constructor, metode copy-from, dll. ) dan tujuan penggunaan array. Tidak ada yang dekat sebersih menggunakan struct.
Yang pasti, sayangnya tidak ada cara yang bagus untuk kelas kontainer selain array untuk menawarkan semantik bersih dari array struct. Yang terbaik bisa dilakukan, jika seseorang ingin koleksi diindeks dengan misalnya string, mungkin akan menawarkan metode "ActOnItem" generik yang akan menerima string untuk indeks, parameter generik, dan delegasi yang akan diteruskan dengan referensi parameter generik dan item koleksi. Itu akan memungkinkan semantik yang hampir sama dengan array array, tetapi kecuali orang-orang vb.net dan C # dapat dikejar untuk menawarkan sintaks yang bagus, kode tersebut akan terlihat kikuk bahkan jika itu adalah kinerja yang wajar (melewati parameter generik akan memungkinkan untuk menggunakan delegasi statis dan akan menghindari kebutuhan untuk membuat instance kelas sementara)
Secara pribadi, saya kesal pada Eric Lippert dkk. memuntahkan tentang jenis nilai yang bisa berubah. Mereka menawarkan semantik yang lebih bersih daripada jenis referensi promiscuous yang digunakan di semua tempat. Meskipun ada beberapa batasan dengan dukungan .net untuk tipe nilai, ada banyak kasus di mana tipe nilai yang dapat berubah lebih cocok daripada jenis entitas lainnya.
sumber
Rectangle
mudah, saya dapat dengan mudah menemukan posisi umum di mana Anda mendapatkan perilaku yang sangat tidak jelas . Pertimbangkan bahwa WinForms mengimplementasikanRectangle
tipe yang bisa berubah yang digunakan diBounds
properti formulir . Jika saya ingin mengubah batasan, saya ingin menggunakan sintaks Anda yang bagus:form.Bounds.X = 10;
Namun, ini justru tidak mengubah apa pun di formulir (dan menghasilkan kesalahan indah yang memberi tahu Anda tentang hal itu). Ketidakkonsistenan adalah kutukan dari pemrograman dan itulah sebabnya kekekalan dibutuhkan.Jenis nilai pada dasarnya mewakili konsep yang tidak berubah. Fx, tidak masuk akal untuk memiliki nilai matematika seperti integer, vektor dll dan kemudian dapat memodifikasinya. Itu akan seperti mendefinisikan kembali makna suatu nilai. Alih-alih mengubah tipe nilai, lebih masuk akal untuk menetapkan nilai unik lainnya. Pikirkan fakta bahwa tipe nilai dibandingkan dengan membandingkan semua nilai propertinya. Intinya adalah bahwa jika sifat-sifatnya sama maka itu adalah representasi universal yang sama dari nilai itu.
Seperti yang Konrad katakan, tidak masuk akal untuk mengubah tanggal juga, karena nilainya mewakili titik waktu yang unik dan bukan turunan dari objek waktu yang memiliki keadaan atau ketergantungan konteks apa pun.
Semoga ini masuk akal bagi Anda. Ini lebih tentang konsep yang Anda coba tangkap dengan tipe nilai daripada detail praktis, untuk memastikan.
sumber
int
iterator, yang akan sama sekali tidak berguna jika itu tidak berubah. Saya pikir Anda sedang mengkonfigurasikan "implementasi kompiler / runtime tipe nilai" dengan "variabel yang diketik ke tipe nilai" - yang terakhir tentu saja bisa berubah ke salah satu nilai yang mungkin.Ada beberapa kasus sudut lain yang dapat menyebabkan perilaku yang tidak terduga dari sudut pandang programmer.
Jenis nilai yang tidak dapat diubah dan bidang yang hanya dapat dibaca
Jenis dan array nilai yang dapat diubah
Misalkan kita memiliki array
Mutable
struct kita dan kita memanggilIncrementI
metode untuk elemen pertama array itu. Perilaku apa yang Anda harapkan dari panggilan ini? Haruskah itu mengubah nilai array atau hanya salinan?Jadi, struct yang bisa berubah tidak jahat selama Anda dan anggota tim lainnya memahami dengan jelas apa yang Anda lakukan. Tetapi ada terlalu banyak kasus sudut ketika perilaku program akan berbeda dari apa yang diharapkan, yang dapat menyebabkan sulit untuk diproduksi dan sulit untuk memahami kesalahan.
sumber
T[]
dan indeks integer, dan dengan ketentuan bahwa akses ke properti tipeArrayRef<T>
akan ditafsirkan sebagai akses ke elemen array yang sesuai) [jika suatu kelas ingin mengekspos suatuArrayRef<T>
untuk tujuan lain apa pun, ia dapat menyediakan metode - yang bertentangan dengan properti - untuk mengambilnya]. Sayangnya, tidak ada ketentuan seperti itu.Jika Anda pernah memprogram dalam bahasa seperti C / C ++, struct boleh digunakan sebagai bisa berubah. Cukup berikan mereka dengan referensi, sekitar dan tidak ada yang salah. Satu-satunya masalah yang saya temukan adalah pembatasan dari kompiler C # dan bahwa, dalam beberapa kasus, saya tidak dapat memaksa hal bodoh untuk menggunakan referensi ke struct, bukan Salin (seperti ketika sebuah struct adalah bagian dari kelas C # ).
Jadi, struct yang bisa berubah tidak jahat, C # telah membuatnya jahat. Saya menggunakan struct bisa berubah dalam C ++ sepanjang waktu dan mereka sangat nyaman dan intuitif. Sebaliknya, C # membuat saya untuk sepenuhnya meninggalkan struct sebagai anggota kelas karena cara mereka menangani objek. Kenyamanan mereka telah merugikan kita.
sumber
readonly
, tetapi jika seseorang menghindari melakukan hal-hal itu bidang kelas tipe struktur baik-baik saja. Satu-satunya batasan struktur yang benar-benar mendasar adalah bahwa bidang struct dari tipe kelas yang dapat berubah sepertiint[]
dapat merangkum identitas atau sekumpulan nilai yang tidak berubah, tetapi tidak dapat digunakan untuk merangkum nilai yang dapat diubah tanpa juga merangkum identitas yang tidak diinginkan.Jika Anda berpegang pada apa yang dimaksudkan untuk struct (dalam C #, Visual Basic 6, Pascal / Delphi, tipe C ++ struct (atau kelas) ketika mereka tidak digunakan sebagai pointer), Anda akan menemukan bahwa struktur tidak lebih dari variabel gabungan . Ini berarti: Anda akan memperlakukan mereka sebagai satu set variabel yang dikemas, dengan nama umum (variabel rekaman yang Anda referensikan dari anggota).
Saya tahu itu akan membingungkan banyak orang yang terbiasa dengan OOP, tetapi itu tidak cukup alasan untuk mengatakan hal-hal seperti itu pada dasarnya jahat, jika digunakan dengan benar. Beberapa struktur tidak dapat diubah karena mereka berniat (ini adalah kasus Python
namedtuple
), tetapi ini adalah paradigma lain yang perlu dipertimbangkan.Ya: struct melibatkan banyak memori, tetapi itu tidak akan menjadi lebih banyak memori dengan melakukan:
dibandingkan dengan:
Konsumsi memori akan setidaknya sama, atau bahkan lebih dalam kasus yang tidak dapat diputuskan (meskipun kasus itu akan bersifat sementara, untuk tumpukan saat ini, tergantung pada bahasa).
Tetapi, akhirnya, struktur adalah struktur , bukan objek. Dalam POO, properti utama suatu objek adalah identitas mereka , yang sebagian besar waktunya tidak lebih dari alamat ingatannya. Struct adalah singkatan dari struktur data (bukan objek yang tepat, dan karenanya mereka tidak memiliki identitas), dan data dapat dimodifikasi. Dalam bahasa lain, catatan (bukan struct , seperti halnya untuk Pascal) adalah kata dan memiliki tujuan yang sama: hanya variabel catatan data, dimaksudkan untuk dibaca dari file, dimodifikasi, dan dibuang ke file (itu adalah utama gunakan dan, dalam banyak bahasa, Anda bahkan dapat mendefinisikan penyelarasan data dalam catatan, sementara itu tidak selalu berarti untuk Objek yang disebut dengan benar).
Ingin contoh yang bagus? Structs digunakan untuk membaca file dengan mudah. Python memiliki pustaka ini karena, karena berorientasi objek dan tidak memiliki dukungan untuk struct, ia harus mengimplementasikannya dengan cara lain, yang agak jelek. Bahasa yang menerapkan struct memiliki fitur itu ... bawaan. Cobalah membaca header bitmap dengan struct yang sesuai dalam bahasa seperti Pascal atau C. Akan mudah (jika struct dibangun dan disejajarkan dengan benar; di Pascal Anda tidak akan menggunakan akses berbasis catatan tetapi berfungsi untuk membaca data biner acak). Jadi, untuk file dan akses memori (lokal) langsung, struct lebih baik daripada objek. Seperti untuk hari ini, kita terbiasa dengan JSON dan XML, jadi kita lupa penggunaan file biner (dan sebagai efek samping, penggunaan struct). Tapi ya: mereka ada, dan punya tujuan.
Mereka tidak jahat. Cukup gunakan untuk tujuan yang benar.
Jika Anda berpikir tentang palu, Anda akan ingin memperlakukan sekrup sebagai paku, untuk menemukan sekrup lebih sulit untuk dipasang di dinding, dan itu akan menjadi kesalahan sekrup, dan mereka akan menjadi yang jahat.
sumber
Bayangkan Anda memiliki array 1.000.000 struct. Setiap struct mewakili ekuitas dengan hal-hal seperti bid_price, offer_price (mungkin desimal) dan seterusnya, ini dibuat oleh C # / VB.
Bayangkan bahwa array dibuat dalam blok memori yang dialokasikan dalam heap yang tidak dikelola sehingga beberapa thread kode asli lainnya dapat secara bersamaan mengakses array (mungkin beberapa kode perf-tinggi melakukan matematika).
Bayangkan kode C # / VB sedang mendengarkan umpan pasar dari perubahan harga, kode itu mungkin harus mengakses beberapa elemen array (untuk keamanan apa pun) dan kemudian memodifikasi beberapa bidang harga.
Bayangkan ini dilakukan puluhan atau bahkan ratusan ribu kali per detik.
Yah mari kita hadapi fakta, dalam hal ini kita benar-benar ingin struct ini bisa berubah, mereka harus karena mereka sedang dibagikan oleh beberapa kode asli lain sehingga membuat salinan tidak akan membantu; mereka perlu karena membuat salinan dari beberapa struct 120 byte pada tingkat ini adalah kegilaan, terutama ketika pembaruan sebenarnya dapat berdampak hanya satu atau dua byte.
Hugo
sumber
Ketika sesuatu dapat bermutasi, ia memperoleh rasa identitas.
Karena
Person
bisa berubah, lebih alami untuk berpikir tentang mengubah posisi Eric daripada mengkloning Eric, memindahkan klon, dan menghancurkan yang asli . Kedua operasi akan berhasil mengubah konteneric.position
, tetapi yang satu lebih intuitif daripada yang lain. Demikian juga, lebih intuitif untuk memberikan Eric sekitar (sebagai referensi) untuk metode untuk memodifikasinya. Memberikan metode klon Eric hampir selalu akan mengejutkan. Siapa pun yang ingin bermutasiPerson
harus ingat untuk meminta referensiPerson
atau mereka akan melakukan hal yang salah.Jika Anda membuat jenisnya tidak dapat diubah, masalahnya hilang; jika saya tidak dapat memodifikasi
eric
, tidak ada bedanya bagi saya apakah saya menerimaeric
atau tiruan darieric
. Secara lebih umum, suatu jenis aman untuk dilewati oleh nilai jika semua negara yang dapat diamati dimiliki oleh anggota yang:Jika kondisi tersebut terpenuhi maka tipe nilai yang dapat berubah berperilaku seperti tipe referensi karena salinan yang dangkal masih akan memungkinkan penerima untuk memodifikasi data asli.
Intuisi kekekalan
Person
tergantung pada apa yang Anda coba lakukan. JikaPerson
hanya mewakili sekumpulan data tentang seseorang, tidak ada yang tidak intuitif tentangnya;Person
variabel benar-benar mewakili nilai abstrak , bukan objek. (Dalam hal itu, mungkin akan lebih tepat untuk mengubah nama menjadiPersonData
.) JikaPerson
sebenarnya memodelkan seseorang itu sendiri, ide untuk terus-menerus membuat dan memindahkan klon adalah konyol bahkan jika Anda telah menghindari perangkap berpikir Anda memodifikasi asli. Dalam hal itu mungkin akan lebih alami untuk hanya membuatPerson
tipe referensi (yaitu, kelas.)Memang, seperti pemrograman fungsional telah mengajarkan kepada kita ada manfaat untuk membuat semuanya berubah (tidak ada yang dapat diam-diam berpegang pada referensi
eric
dan memutasinya), tetapi karena itu bukan idiomatik dalam OOP itu masih akan menjadi tidak intuitif untuk orang lain yang bekerja dengan Anda kode.sumber
foo
memegang satu-satunya referensi ke targetnya di mana saja di alam semesta, dan tidak ada yang menangkap nilai identitas-hash objek itu, maka bidang mutasifoo.X
secara semantik setara dengan membuatfoo
titik ke objek baru yang sama seperti yang sebelumnya disebut, tetapi denganX
memegang nilai yang diinginkan. Dengan tipe kelas, umumnya sulit untuk mengetahui apakah banyak referensi ada untuk sesuatu, tetapi dengan struct itu mudah: mereka tidak.Thing
adalah tipe kelas yang dapat diubah, sebuahThing[]
akan merangkum identitas objek - apakah orang menginginkannya atau tidak - kecuali seseorang dapat memastikan bahwa tidak adaThing
dalam array yang ada referensi luar akan pernah dimutasi. Jika seseorang tidak ingin elemen array untuk merangkum identitas, seseorang harus secara umum memastikan bahwa tidak ada item yang dipegang referensi akan pernah dimutasi, atau bahwa tidak ada referensi luar yang akan ada untuk item yang dipegang [pendekatan hybrid juga dapat bekerja ] Tidak ada pendekatan yang sangat nyaman. JikaThing
adalah struktur, aThing[]
merangkum nilai saja.Itu tidak ada hubungannya dengan struct (dan tidak dengan C #, baik) tetapi di Jawa Anda mungkin mendapatkan masalah dengan objek yang bisa berubah ketika mereka misalnya kunci dalam peta hash. Jika Anda mengubahnya setelah menambahkannya ke peta dan itu mengubah kode hash , hal-hal jahat mungkin terjadi.
sumber
Secara pribadi ketika saya melihat kode berikut ini terlihat cukup kikuk bagi saya:
data.value.set (data.value.get () + 1);
bukan hanya
data.value ++; atau data.value = data.value + 1;
Enkapsulasi data berguna ketika melewati kelas di sekitar dan Anda ingin memastikan nilai dimodifikasi secara terkendali. Namun ketika Anda memiliki set publik dan mendapatkan fungsi yang tidak lebih dari menetapkan nilai pada apa yang pernah diteruskan, bagaimana ini merupakan peningkatan dari sekadar meneruskan struktur data publik?
Ketika saya membuat struktur pribadi di dalam kelas, saya membuat struktur itu untuk mengatur satu set variabel menjadi satu kelompok. Saya ingin dapat memodifikasi struktur itu dalam ruang lingkup kelas, tidak mendapatkan salinan dari struktur itu dan membuat contoh baru.
Bagi saya ini mencegah penggunaan yang valid dari struktur yang digunakan untuk mengatur variabel publik, jika saya ingin kontrol akses saya akan menggunakan kelas.
sumber
Ada beberapa masalah dengan contoh Mr. Eric Lippert. Ini dibuat untuk menggambarkan titik bahwa struct disalin dan bagaimana itu bisa menjadi masalah jika Anda tidak hati-hati. Melihat contoh saya melihatnya sebagai hasil dari kebiasaan pemrograman yang buruk dan tidak benar-benar masalah dengan struct atau kelas.
Seorang struct seharusnya hanya memiliki anggota publik dan tidak perlu enkapsulasi. Jika ya maka itu benar-benar harus tipe / kelas. Anda benar-benar tidak perlu dua konstruksi untuk mengatakan hal yang sama.
Jika Anda memiliki kelas yang menyertakan struct, Anda akan memanggil metode di kelas untuk memutasi struct member. Inilah yang akan saya lakukan sebagai kebiasaan pemrograman yang baik.
Implementasi yang tepat adalah sebagai berikut.
Sepertinya itu adalah masalah dengan kebiasaan pemrograman yang bertentangan dengan masalah dengan struct itu sendiri. Structs seharusnya bisa berubah, itulah ide dan maksudnya.
Hasil dari perubahan voila berlaku seperti yang diharapkan:
1 2 3 Tekan tombol apa saja untuk melanjutkan. . .
sumber
Ada banyak keuntungan dan kerugian dari data yang bisa berubah. Kerugian jutaan dolar adalah aliasing. Jika nilai yang sama digunakan di banyak tempat, dan salah satunya mengubahnya, maka secara ajaib akan berubah menjadi tempat lain yang menggunakannya. Ini terkait dengan, tetapi tidak identik dengan, kondisi lomba.
Keuntungan jutaan dolar adalah modularitas, kadang-kadang. Status yang dapat diubah dapat memungkinkan Anda untuk menyembunyikan informasi yang berubah dari kode yang tidak perlu diketahui tentang hal itu.
Art of the Interpreter membahas trade off ini secara rinci, dan memberikan beberapa contoh.
sumber
Saya tidak percaya mereka jahat jika digunakan dengan benar. Saya tidak akan memasukkannya ke dalam kode produksi saya, tetapi saya akan melakukan sesuatu seperti mengolok-olok pengujian unit terstruktur, di mana umur sebuah struct relatif kecil.
Menggunakan contoh Eric, mungkin Anda ingin membuat contoh kedua dari Eric itu, tetapi buat penyesuaian, karena itulah sifat pengujian Anda (yaitu duplikasi, lalu modifikasi). Tidak masalah apa yang terjadi dengan instance pertama Eric jika kita hanya menggunakan Eric2 untuk sisa skrip pengujian, kecuali jika Anda berencana menggunakannya sebagai perbandingan pengujian.
Ini akan sangat berguna untuk menguji atau memodifikasi kode legacy yang dangkal mendefinisikan objek tertentu (titik struct), tetapi dengan memiliki struct yang tidak dapat diubah, ini mencegah penggunaannya secara mengganggu.
sumber
Range<T>
tipe dengan anggotaMinimum
danMaximum
bidang tipeT
, dan kodeRange<double> myRange = foo.getRange();
, jaminan apa pun tentang apaMinimum
dan isiMaximum
harus berasalfoo.GetRange();
. MemilikiRange
struct bidang terbuka akan membuat jelas bahwa itu tidak akan menambah perilaku sendiri.