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:
struct PointyStruct {public int x, y, z;};
kelas PointyClass {public int x, y, z;};
membatalkan Method1 (PointyStruct foo);
membatalkan Method2 (ref PointyStruct foo);
membatalkan Method3 (PointyClass foo);
Untuk setiap metode, jawab pertanyaan berikut:
- Dengan asumsi metode ini tidak menggunakan kode "tidak aman", mungkinkah itu memodifikasi foo?
- Jika tidak ada referensi luar ke 'foo' sebelum metode dipanggil, bisakah referensi luar ada setelah?
Jawaban:
Pertanyaan 1::
Method1()
tidak (maksud jelas)
Method2()
: ya (niat jelas)
Method3()
: ya (niat tidak pasti)
Pertanyaan 2
Method1()
:: tidak
Method2()
: tidak (kecuali tidak aman)
Method3()
: ya
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.
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.