Apakah kekekalan sangat bermanfaat ketika tidak ada konkurensi?


53

Tampaknya keamanan benang selalu / sering disebutkan sebagai manfaat utama menggunakan jenis yang tidak dapat diubah dan terutama koleksi.

Saya memiliki situasi di mana saya ingin memastikan bahwa suatu metode tidak akan mengubah kamus string (yang tidak dapat diubah dalam C #). Saya ingin membatasi hal-hal sebanyak mungkin.

Namun saya tidak yakin apakah menambahkan ketergantungan pada paket baru (Microsoft Immutable Collections) layak dilakukan. Kinerja juga bukan masalah besar.

Jadi saya kira pertanyaan saya adalah apakah koleksi yang tidak dapat diubah sangat disarankan untuk saat tidak ada persyaratan kinerja yang sulit dan tidak ada masalah keamanan utas? Pertimbangkan bahwa semantik nilai (seperti dalam contoh saya) mungkin atau mungkin tidak menjadi persyaratan juga.


1
Modifikasi bersamaan tidak harus berarti utas. Lihat saja nama aptly ConcurrentModificationExceptionyang biasanya disebabkan oleh utas yang sama memutasikan koleksi di utas yang sama, di badan foreachloop di atas koleksi yang sama.

1
Maksud saya Anda tidak salah, tapi itu berbeda dari yang diminta OP. Pengecualian itu dilemparkan karena modifikasi selama pencacahan tidak diperbolehkan. Menggunakan ConcurrentDictionary, misalnya, masih akan memiliki kesalahan ini.
edthethird

13
Mungkin Anda juga harus bertanya pada diri sendiri pertanyaan sebaliknya: kapan sifat berubah itu berharga?
Giorgio

2
Di Jawa, jika berubah-ubah mempengaruhi hashCode()atau equals(Object)mengakibatkan perubahan, ini dapat menyebabkan kesalahan saat menggunakan Collections(misalnya, dalam suatu HashSetobjek disimpan dalam "ember" dan setelah bermutasi harus pergi ke yang lain).
SJuan76

2
@ DavorŽdralo Sejauh abstraksi bahasa tingkat tinggi pergi, ketidakberdayaan meresap agak jinak. Ini hanyalah perpanjangan alami dari abstraksi yang sangat umum (bahkan ada dalam C) untuk menciptakan, dan secara diam-diam membuang, "nilai sementara". Mungkin Anda bermaksud mengatakan bahwa ini adalah cara yang tidak efisien untuk menggunakan CPU, tetapi argumen itu memiliki kelemahan juga: Mutabilitas-senang tetapi bahasa dinamis sering berkinerja lebih buruk daripada hanya-statis tetapi bahasa statis, sebagian karena ada beberapa yang pintar (tapi akhirnya cukup sederhana) trik untuk mengoptimalkan program juggling data abadi: tipe linear, deforestasi, dll.

Jawaban:


101

Kekekalan menyederhanakan jumlah informasi yang Anda perlu lacak secara mental saat membaca kode nanti . Untuk variabel yang bisa berubah, dan terutama anggota kelas yang bisa berubah, sangat sulit untuk mengetahui status mereka pada baris tertentu yang Anda baca, tanpa menjalankan kode dengan debugger. Data yang tidak dapat diubah mudah untuk dipikirkan - akan selalu sama. Jika Anda ingin mengubahnya, Anda harus membuat nilai baru.

Jujur saya lebih suka membuat hal-hal yang tidak dapat diubah secara default , dan kemudian mengubahnya menjadi bisa berubah di mana terbukti bahwa mereka perlu, apakah ini berarti Anda memerlukan kinerja, atau algoritma yang Anda miliki tidak masuk akal untuk kekekalan.


23
+1 Concurrency adalah mutasi simultan, tetapi mutasi yang menyebar dari waktu ke waktu dapat sama sulitnya untuk dipikirkan
guillaume31

20
Untuk memperluas ini: fungsi yang bergantung pada variabel yang bisa berubah dapat dianggap sebagai mengambil argumen tersembunyi tambahan, yang merupakan nilai saat ini dari variabel yang bisa diubah. Setiap fungsi yang mengubah variabel yang dapat berubah juga dapat dianggap sebagai menghasilkan nilai pengembalian tambahan, yaitu nilai baru dari keadaan yang dapat diubah. Ketika melihat sepotong kode Anda tidak tahu apakah itu tergantung pada atau mengubah keadaan yang bisa berubah sehingga Anda harus mencari tahu dan kemudian melacak perubahan secara mental. Ini juga memperkenalkan kopling antara dua bagian kode yang berbagi keadaan dapat berubah, dan kopling buruk.
Doval

29
@Mehrdad People juga berhasil membuat program besar dalam perakitan selama beberapa dekade. Kemudian kami melakukan beberapa dekade C.
Doval

11
@Mehrdad Menyalin seluruh objek bukanlah pilihan yang baik saat objek besar. Saya tidak melihat mengapa urutan besarnya yang terlibat dalam masalah peningkatan. Apakah Anda akan menolak peningkatan 20% (catatan: angka arbitrer) dalam produktivitas hanya karena itu bukan peningkatan tiga digit? Kekekalan adalah standar yang waras ; Anda dapat menyimpang dari itu, tetapi Anda perlu alasan.
Doval

9
@Giorgio Scala membuat saya sadar betapa jarangnya Anda perlu membuat nilai bisa berubah. Setiap kali saya menggunakan bahasa itu, saya membuat semuanya menjadi val, dan hanya pada kesempatan yang sangat jarang saya menemukan bahwa saya perlu mengubah sesuatu menjadi a var. Banyak 'variabel' yang saya definisikan dalam bahasa apa pun hanya ada untuk menyimpan nilai yang menyimpan hasil dari beberapa perhitungan, dan tidak perlu diperbarui.
KChaloux

22

Kode Anda harus menyatakan niat Anda. Jika Anda tidak ingin objek diubah setelah dibuat, buat tidak mungkin untuk dimodifikasi.

Kekekalan memiliki beberapa manfaat:

  • Niat penulis asli dinyatakan lebih baik.

    Bagaimana Anda bisa tahu bahwa dalam kode berikut, memodifikasi nama akan menyebabkan aplikasi menghasilkan pengecualian di suatu tempat nanti?

    public class Product
    {
        public string Name { get; set; }
    
        ...
    }
    
  • Lebih mudah untuk memastikan bahwa objek tidak akan muncul dalam keadaan tidak valid.

    Anda harus mengontrol ini di konstruktor, dan hanya di sana. Di sisi lain, jika Anda memiliki banyak setter dan metode yang memodifikasi objek, kontrol tersebut mungkin menjadi sangat sulit, terutama ketika, misalnya, dua bidang harus berubah pada saat yang sama agar objek menjadi valid.

    Misalnya, objek valid jika alamatnya bukan null atau koordinat GPS tidak null, tetapi tidak valid jika alamat dan koordinat GPS ditentukan. Bisakah Anda bayangkan untuk memvalidasi ini jika alamat dan koordinat GPS memiliki setter, atau keduanya bisa berubah?

  • Konkurensi.

Omong-omong, dalam kasus Anda, Anda tidak memerlukan paket pihak ketiga. NET Framework. Sudah termasuk ReadOnlyDictionary<TKey, TValue>kelas.


1
+1, terutama untuk "Anda harus mengontrol ini di konstruktor, dan hanya di sana." IMO ini adalah keuntungan yang sangat besar.
Giorgio

10
Manfaat lain: menyalin objek adalah gratis. Hanya sebuah pointer.
Robert Grant

1
@MainMa Terima kasih atas jawaban Anda, tetapi sejauh yang saya mengerti, ReadOnlyDictionary tidak memberikan jaminan bahwa orang lain tidak akan mengubah kamus yang mendasarinya (bahkan tanpa konkurensi saya mungkin ingin menyimpan referensi ke kamus asli di dalam objek, metode ini termasuk di dalamnya. untuk digunakan nanti). ReadOnlyDictionary bahkan dideklarasikan dalam namespace yang aneh: System.Collections.ObjectModel.
Den

2
@Den: Itu berhubungan dengan salah satu kencing peliharaan saya: orang-orang tentang "read-only" dan "immutable" sebagai sinonim. Jika suatu objek dienkapsulasi dalam pembungkus read-only dan tidak ada referensi lain yang ada atau dipertahankan di mana pun di alam semesta maka membungkus objek akan membuatnya tidak berubah, dan referensi ke pembungkus dapat digunakan sebagai steno untuk merangkum keadaan dari benda yang terkandung di dalamnya. Namun, tidak ada mekanisme kode yang dapat memastikan apakah itu yang terjadi. Sebaliknya, karena pembungkus menyembunyikan jenis objek yang dibungkus, membungkus benda yang tidak dapat berubah ...
supercat

2
... akan membuat kode tidak mungkin mengetahui apakah bungkus yang dihasilkan dapat dengan aman dianggap tidak dapat diubah.
supercat

13

Ada banyak alasan utas tunggal untuk menggunakan imutabilitas. Sebagai contoh

Objek A berisi Objek B.

Kode eksternal menanyakan Objek B Anda dan Anda mengembalikannya.

Sekarang Anda memiliki tiga kemungkinan situasi:

  1. B tidak berubah, tidak ada masalah.
  2. B bisa berubah, Anda membuat salinan defensif dan mengembalikannya. Performa terpukul tetapi tidak ada risiko.
  3. B bisa berubah, Anda mengembalikannya.

Dalam kasus ketiga kode pengguna mungkin tidak menyadari apa yang telah Anda lakukan dan dapat membuat perubahan pada objek, dan dengan demikian mengubah data internal objek Anda dengan Anda tidak memiliki kontrol atau visibilitas dari hal itu terjadi.


9

Ketidakmampuan juga dapat sangat menyederhanakan implementasi pemulung. Dari wiki GHC :

[...] Ketidakberdayaan data memaksa kita untuk menghasilkan banyak data sementara tetapi juga membantu mengumpulkan sampah ini dengan cepat. Kuncinya adalah bahwa data yang kekal TIDAK PERNAH menunjuk ke nilai yang lebih muda. Memang, nilai yang lebih muda belum ada pada saat nilai lama dibuat, sehingga tidak dapat diarahkan ke awal. Dan karena nilai-nilai tidak pernah dimodifikasi, tidak juga dapat ditunjukkan nanti. Ini adalah properti utama dari data yang tidak dapat diubah.

Ini sangat menyederhanakan pengumpulan sampah (GC). Kapan saja kita dapat memindai nilai-nilai terakhir yang dibuat dan membebaskan nilai-nilai yang tidak ditunjuk dari set yang sama (tentu saja, akar nyata hierarki nilai langsung tinggal di tumpukan). [...] Jadi ia memiliki perilaku kontra-intuitif: semakin besar persentase nilai Anda adalah sampah - semakin cepat kerjanya. [...]


5

Memperluas apa yang diringkas oleh KChaloux dengan sangat baik ...

Idealnya, Anda memiliki dua jenis bidang, dan karenanya dua jenis kode menggunakannya. Masing-masing bidang tidak dapat diubah, dan kode tidak harus mempertimbangkan mutabilitas; atau bidang dapat diubah, dan kami perlu menulis kode yang mengambil snapshot ( int x = p.x) atau dengan anggun menangani perubahan tersebut.

Dalam pengalaman saya, sebagian besar kode berada di antara keduanya, menjadi kode optimis : itu dengan bebas merujuk data yang bisa berubah, dengan asumsi bahwa panggilan pertama ke p.xakan memiliki hasil yang sama dengan panggilan kedua. Dan sebagian besar waktu, ini benar, kecuali ketika ternyata tidak lagi. Ups.

Jadi, sungguh, balikkan pertanyaan itu: Apa alasan saya membuat ini bisa berubah ?

  • Mengurangi alokasi memori / gratis?
  • Secara alami bisa berubah? (mis. penghitung)
  • Menghemat pengubah, kebisingan horizontal? (const / final)
  • Membuat beberapa kode lebih pendek / mudah? (init default, mungkin ditimpa setelah)

Apakah Anda menulis kode defensif? Kekekalan akan menghemat beberapa penyalinan. Apakah Anda menulis kode optimis? Kekekalan akan menghindarkan Anda dari kegilaan dari serangga aneh dan mustahil itu.


3

Manfaat lain dari kekekalan adalah bahwa itu adalah langkah pertama dalam mengumpulkan benda-benda yang tidak dapat diubah ini menjadi kumpulan. Kemudian Anda bisa mengelolanya sehingga Anda tidak membuat banyak objek yang secara konseptual dan semantik mewakili hal yang sama. Contoh yang baik adalah String Java.

Ini adalah fenomena linguistik yang terkenal bahwa beberapa kata muncul banyak, mungkin muncul dalam konteks lain juga. Jadi, alih-alih membuat beberapa Stringobjek, Anda bisa menggunakan satu objek yang tidak berubah. Tapi kemudian Anda perlu menjaga manajer kolam untuk merawat benda-benda abadi ini.

Ini akan menghemat banyak memori. Ini adalah artikel yang menarik untuk dibaca juga: http://en.wikipedia.org/wiki/Zipf%27s_law


1

Di Jawa, C #, dan bahasa lain yang serupa, bidang tipe kelas dapat digunakan untuk mengidentifikasi objek, atau untuk merangkum nilai atau status dalam objek tersebut, tetapi bahasa tidak membuat perbedaan antara penggunaan tersebut. Misalkan objek kelas Georgememiliki bidang tipe char[] chars;. Bidang itu dapat merangkum urutan karakter di:

  1. Array yang tidak akan pernah dimodifikasi, atau terpapar pada kode apa pun yang mungkin memodifikasinya, tetapi referensi luarnya mungkin ada.

  2. Array yang tidak memiliki referensi luar, tetapi yang dapat dimodifikasi secara bebas oleh George.

  3. Array yang dimiliki oleh George, tetapi yang mungkin ada pandangan luar yang diharapkan untuk mewakili keadaan George saat ini.

Selain itu, variabel dapat, alih-alih merangkum urutan karakter, merangkum tampilan langsung ke urutan karakter yang dimiliki oleh beberapa objek lain

Jika charssaat ini merangkum urutan karakter [angin], dan George ingin charsmerangkum urutan karakter [tongkat], ada beberapa hal yang bisa dilakukan George:

A. Buat array baru yang berisi karakter [tongkat] dan ubah charsuntuk mengidentifikasi array itu daripada yang lama.

B. Identifikasi entah bagaimana array karakter yang sudah ada sebelumnya yang akan selalu memegang karakter [tongkat] dan berubah charsuntuk mengidentifikasi array itu daripada yang lama.

C. Ubah karakter kedua array yang diidentifikasi oleh charsmenjadi a.

Dalam kasus 1, (A) dan (B) adalah cara yang aman untuk mencapai hasil yang diinginkan. Dalam kasus (2), (A) dan (C) aman, tetapi (B) tidak akan [itu tidak akan menyebabkan masalah langsung, tetapi karena George akan menganggap bahwa ia memiliki kepemilikan array, ia akan menganggap bahwa ia memiliki dapat mengubah array sesuka]. Dalam kasus (3), pilihan (A) dan (B) akan mematahkan pandangan luar, dan hanya pilihan (C) yang benar. Jadi, mengetahui bagaimana mengubah urutan karakter yang dienkapsulasi oleh bidang membutuhkan pengetahuan jenis bidang semantik apa itu.

Jika alih-alih menggunakan bidang tipe char[], yang merangkum urutan karakter yang berpotensi bisa berubah, kode menggunakan tipe String, yang merangkum urutan karakter yang tidak berubah, semua masalah di atas hilang. Semua bidang tipe Stringmerangkum urutan karakter menggunakan objek sharable yang tidak akan pernah berubah. Alhasil, jika bidang jenisStringmerangkum "angin", satu-satunya cara untuk membuatnya merangkum "tongkat" adalah untuk membuatnya mengidentifikasi objek yang berbeda - yang memegang "tongkat". Dalam kasus di mana kode menyimpan satu-satunya referensi ke objek, bermutasi objek mungkin lebih efisien daripada membuat yang baru, tetapi setiap kali kelas bisa berubah, perlu untuk membedakan antara berbagai cara yang dengannya ia dapat merangkum nilai. Secara pribadi saya pikir Apps Hungaria seharusnya digunakan untuk ini (saya akan mempertimbangkan empat penggunaan char[]menjadi tipe yang berbeda secara semantik, meskipun sistem jenis menganggapnya identik - persis seperti situasi di mana Apps Hungaria bersinar), tetapi karena itu bukan cara termudah untuk menghindari ambiguitas seperti itu untuk merancang tipe yang tidak dapat diubah yang hanya merangkum nilai satu arah.


Sepertinya jawaban yang masuk akal, tetapi agak sulit dibaca dan dipahami.
Den

1

Ada beberapa contoh yang bagus di sini, tetapi saya ingin bergabung dengan beberapa contoh pribadi di mana ketidakberdayaan membantu satu ton. Dalam kasus saya, saya mulai merancang struktur data konkuren yang tidak dapat diubah terutama dengan harapan hanya bisa menjalankan kode secara paralel dengan tumpang tindih membaca dan menulis dan tidak harus khawatir tentang kondisi balapan. Ada ceramah yang diberikan John Carmack semacam itu yang mengilhami saya untuk melakukannya di mana ia berbicara tentang gagasan semacam itu. Ini adalah struktur yang cukup mendasar dan cukup sepele untuk diterapkan seperti ini:

masukkan deskripsi gambar di sini

Tentu saja dengan beberapa lonceng dan peluit di sana seperti bisa menghilangkan elemen dalam waktu yang konstan dan meninggalkan lubang yang dapat direklamasi di belakang dan membuat balok dihilangkan jika mereka menjadi kosong dan berpotensi dibebaskan untuk contoh abadi yang diberikan. Tetapi pada dasarnya untuk memodifikasi struktur, Anda memodifikasi versi "sementara" dan secara atomis melakukan perubahan yang Anda buat untuk mendapatkan salinan abadi yang tidak menyentuh yang lama, dengan versi baru hanya membuat salinan baru dari blok yang harus dibuat unik saat menyalin dangkal dan referensi menghitung yang lain.

Namun, saya tidak menemukan itu bahwaberguna untuk keperluan multithreading. Bagaimanapun, masih ada masalah konseptual di mana, katakanlah, sistem fisika menerapkan fisika secara bersamaan ketika seorang pemain mencoba untuk memindahkan elemen di dalam dunia. Salinan data transformasi yang tidak berubah yang Anda ikuti, yang ditransformasikan oleh pemain atau yang ditransformasikan oleh sistem fisika? Jadi saya belum benar-benar menemukan solusi yang bagus dan sederhana untuk masalah konseptual dasar ini kecuali untuk memiliki struktur data yang bisa berubah yang hanya mengunci dengan cara yang lebih cerdas dan mencegah tumpang tindih membaca dan menulis ke bagian yang sama dari buffer untuk menghindari kemacetan utas. Itu sesuatu yang tampaknya John Carmack telah menemukan cara untuk menyelesaikannya dalam permainannya; setidaknya dia membicarakannya seperti dia hampir bisa melihat solusi tanpa membuka mobil cacing. Saya belum sampai sejauh dia dalam hal itu. Yang bisa saya lihat adalah pertanyaan desain tanpa akhir jika saya mencoba untuk memaralelkan semua yang ada di sekitar yang tidak berubah. Saya berharap saya bisa menghabiskan satu hari mencerna otaknya karena sebagian besar usaha saya dimulai dengan ide-ide yang dia buang.

Namun demikian, saya menemukan nilai yang sangat besar dari struktur data yang tidak dapat diubah ini di bidang lain. Saya bahkan menggunakannya sekarang untuk menyimpan gambar yang benar-benar aneh dan memang membuat akses acak memerlukan beberapa instruksi lagi (shift kanan dan bitwise andbersama dengan lapisan tipuan pointer), tapi saya akan membahas manfaat di bawah ini.

Batalkan Sistem

Salah satu tempat paling cepat yang saya temukan untuk mendapat manfaat dari ini adalah sistem undo. Kode sistem undo dulu merupakan salah satu hal yang paling rawan kesalahan di area saya (industri FX visual), dan tidak hanya dalam produk yang saya kerjakan tetapi dalam produk pesaing (sistem undo mereka juga terkelupas) karena ada begitu banyak perbedaan jenis data yang perlu dikhawatirkan akan dibatalkan dan diulang dengan benar (sistem properti, perubahan data mesh, perubahan shader yang tidak berbasis properti seperti bertukar satu dengan yang lain, perubahan hierarki adegan seperti mengubah induk dari anak, perubahan gambar / tekstur, dll. dll.).

Jadi jumlah kode undo yang diperlukan sangat besar, sering menyaingi jumlah kode yang mengimplementasikan sistem yang sistem undo harus mencatat perubahan keadaan. Dengan bersandar pada struktur data ini, saya bisa mengembalikan sistem undo menjadi seperti ini:

on user operation:
    copy entire application state to undo entry
    perform operation

on undo/redo:
    swap application state with undo entry

Biasanya kode di atas akan sangat tidak efisien ketika data adegan Anda membentang gigabytes untuk menyalinnya secara keseluruhan. Tetapi struktur data ini hanya menyalin hal-hal dangkal yang tidak berubah, dan itu sebenarnya membuatnya cukup murah menyimpan salinan seluruh kondisi aplikasi yang tidak berubah. Jadi sekarang saya dapat mengimplementasikan sistem undo semudah kode di atas dan hanya fokus menggunakan struktur data yang tidak berubah ini untuk membuat penyalinan bagian yang tidak berubah dari status aplikasi lebih murah dan lebih murah dan lebih murah. Sejak saya mulai menggunakan struktur data ini, semua proyek pribadi saya memiliki sistem undo hanya menggunakan pola sederhana ini.

Sekarang masih ada beberapa overhead di sini. Terakhir kali saya mengukur sekitar 10 kilobyte hanya untuk menyalin seluruh kondisi aplikasi tanpa membuat perubahan apa pun (ini tidak tergantung pada kompleksitas adegan karena adegan diatur dalam hierarki, jadi jika tidak ada yang di bawah perubahan root, hanya root dangkal disalin tanpa harus turun ke anak-anak). Itu jauh dari 0 byte seperti yang diperlukan untuk sistem undo yang hanya menyimpan delta. Tetapi pada 10 kilobyte membatalkan overhead per operasi, itu masih hanya megabyte per 100 operasi pengguna. Ditambah lagi, saya masih bisa menekannya lebih jauh di masa depan jika diperlukan.

Pengecualian-Keamanan

Pengecualian keamanan dengan aplikasi yang kompleks bukanlah masalah sepele. Namun, ketika status aplikasi Anda tidak berubah dan Anda hanya menggunakan objek sementara untuk mencoba melakukan transaksi perubahan atom, maka itu pada dasarnya pengecualian-aman karena jika ada bagian dari kode yang dilemparkan, transien dibuang sebelum memberikan salinan baru yang tidak dapat diubah . Sehingga meremehkan salah satu hal tersulit yang selalu saya temukan untuk mendapatkan yang benar dalam basis kode C ++ kompleks.

Terlalu banyak orang sering hanya menggunakan sumber daya yang sesuai dengan RAII di C ++ dan berpikir itu sudah cukup untuk menjadi pengecualian-aman. Seringkali tidak, karena suatu fungsi umumnya dapat menyebabkan efek samping untuk menyatakan di luar yang lokal untuk ruang lingkupnya. Anda biasanya harus mulai berurusan dengan pelindung lingkup dan logika rollback canggih dalam kasus tersebut. Struktur data ini membuatnya jadi saya sering tidak perlu repot dengan itu karena fungsinya tidak menyebabkan efek samping. Mereka mengembalikan salinan status aplikasi yang tidak berubah dan bukannya mengubah status aplikasi.

Pengeditan Tidak Merusak

masukkan deskripsi gambar di sini

Pengeditan non-destruktif pada dasarnya adalah operasi layering / stacking / menghubungkan bersama tanpa menyentuh data pengguna asli (hanya input data dan data output tanpa menyentuh input). Biasanya sepele untuk diterapkan dengan aplikasi gambar sederhana seperti Photoshop dan mungkin tidak mendapat manfaat sebanyak itu dari struktur data ini karena banyak operasi mungkin hanya ingin mengubah setiap piksel dari keseluruhan gambar.

Namun, dengan editing mesh non-destruktif, misalnya, banyak operasi sering ingin mengubah hanya sebagian dari mesh. Satu operasi mungkin hanya ingin memindahkan beberapa simpul di sini. Yang lain mungkin hanya ingin membagi beberapa poligon di sana. Di sini struktur data yang tidak berubah membantu satu ton dalam menghindari kebutuhan untuk harus membuat seluruh salinan seluruh mesh hanya untuk mengembalikan versi baru dari mesh dengan sebagian kecil dari itu berubah.

Meminimalkan Efek Samping

Dengan struktur ini di tangan, itu juga membuatnya mudah untuk menulis fungsi yang meminimalkan efek samping tanpa menimbulkan penalti kinerja yang besar untuk itu. Saya mendapati diri saya semakin banyak menulis fungsi yang hanya mengembalikan seluruh struktur data yang tidak dapat diubah dengan nilai hari ini tanpa menimbulkan efek samping, bahkan ketika itu tampak agak boros.

Sebagai contoh, biasanya godaan untuk mengubah banyak posisi mungkin untuk menerima matriks dan daftar objek dan mengubah mereka dengan cara yang bisa berubah. Hari-hari ini saya menemukan diri saya baru saja mengembalikan daftar objek baru.

Ketika Anda memiliki lebih banyak fungsi seperti ini di sistem Anda yang tidak menimbulkan efek samping, itu pasti membuatnya lebih mudah untuk mempertimbangkan kebenarannya serta menguji kebenarannya.

Manfaat Salinan Murah

Jadi bagaimanapun, ini adalah area di mana saya menemukan yang paling banyak digunakan dari struktur data yang tidak dapat diubah (atau struktur data yang persisten). Saya juga agak terlalu bersemangat pada awalnya dan membuat pohon yang tidak bisa diubah dan daftar tertaut yang tidak berubah dan tabel hash yang tidak dapat berubah, tetapi seiring waktu saya jarang menemukan banyak kegunaan untuk itu. Saya terutama menemukan sebagian besar penggunaan array mirip chunky yang tidak bisa diubah dalam diagram di atas.

Saya juga masih memiliki banyak kode yang dapat digunakan dengan mutables (merasa ini adalah kebutuhan praktis setidaknya untuk kode tingkat rendah), tetapi status aplikasi utama adalah hierarki yang tidak dapat diubah, menelusuri dari adegan yang tidak dapat diubah ke komponen yang tidak dapat diubah di dalamnya. Beberapa komponen yang lebih murah masih disalin secara penuh, tetapi yang paling mahal seperti jerat dan gambar menggunakan struktur yang tidak dapat diubah untuk memungkinkan salinan sebagian murah itu hanya dari bagian-bagian yang perlu diubah.


0

Sudah banyak jawaban bagus. Ini hanyalah info tambahan yang agak spesifik untuk .NET. Saya sedang menggali posting blog .NET lama dan menemukan ringkasan manfaat dari sudut pandang pengembang Microsoft Immutable Collections:

  1. Semantik foto, memungkinkan Anda berbagi koleksi dengan cara yang bisa diandalkan penerima agar tidak pernah berubah.

  2. Keamanan utir implisit dalam aplikasi multi-utas (tidak diperlukan kunci untuk mengakses koleksi).

  3. Kapan pun Anda memiliki anggota kelas yang menerima atau mengembalikan jenis koleksi dan Anda ingin menyertakan semantik baca-saja dalam kontrak.

  4. Ramah pemrograman fungsional.

  5. Izinkan modifikasi koleksi selama enumerasi, sambil memastikan koleksi asli tidak berubah.

  6. Mereka menerapkan antarmuka IReadOnly * yang sama dengan yang sudah ditangani kode Anda sehingga migrasi menjadi mudah.

Jika seseorang memberi Anda ReadOnlyCollection, IReadOnlyList, atau IEnumerable, satu-satunya jaminan adalah Anda tidak dapat mengubah data - tidak ada jaminan bahwa orang yang menyerahkan koleksi tidak akan mengubahnya. Namun, Anda sering membutuhkan keyakinan bahwa itu tidak akan berubah. Jenis-jenis ini tidak menawarkan acara untuk memberi tahu Anda ketika kontennya berubah, dan jika mereka berubah, mungkinkah itu terjadi pada utas yang berbeda, mungkin saat Anda sedang menghitung isinya? Perilaku seperti itu akan menyebabkan korupsi data dan / atau pengecualian acak dalam aplikasi Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.