Saya menggunakan properti yang diimplementasikan secara otomatis. Saya kira cara tercepat untuk memperbaiki yang berikut adalah dengan mendeklarasikan variabel dukungan saya sendiri?
public Point Origin { get; set; }
Origin.X = 10; // fails with CS1612
Pesan Galat: Tidak dapat mengubah nilai balik 'ekspresi' karena ini bukan variabel
Upaya dilakukan untuk mengubah tipe nilai yang merupakan hasil dari ekspresi perantara. Karena nilainya tidak bertahan, nilainya tidak akan berubah.
Untuk mengatasi kesalahan ini, simpan hasil ekspresi dalam nilai perantara, atau gunakan tipe referensi untuk ekspresi perantara.
c#
variables
struct
immutability
P aul
sumber
sumber
Jawaban:
Ini karena
Point
merupakan tipe nilai (struct
).Karena itu, ketika Anda mengakses
Origin
properti Anda mengakses salinan nilai yang dipegang oleh kelas, bukan nilai itu sendiri seperti yang Anda lakukan dengan tipe referensi (class
), jadi jika Anda menyetelX
properti di atasnya maka Anda menetapkan properti pada salinan dan kemudian membuangnya, meninggalkan nilai asli tidak berubah. Ini mungkin bukan yang Anda maksudkan, itulah sebabnya kompiler memperingatkan Anda tentang hal itu.Jika Anda ingin mengubah
X
nilainya saja, Anda perlu melakukan sesuatu seperti ini:sumber
Menggunakan variabel pendukung tidak akan membantu.ThePoint
tipe adalah tipe Nilai.Anda perlu menetapkan seluruh nilai Poin ke properti Origin: -
Masalahnya adalah bahwa ketika Anda mengakses properti Origin apa yang dikembalikan oleh
get
adalah salinan struktur Point di bidang properti Origin yang dibuat secara otomatis. Oleh karena itu modifikasi bidang X Anda salinan ini tidak akan mempengaruhi bidang yang mendasarinya. Compiler mendeteksi ini dan memberi Anda kesalahan karena operasi ini sama sekali tidak berguna.Bahkan jika Anda menggunakan variabel dukungan Anda sendiri Anda
get
akan terlihat seperti: -Anda masih akan mengembalikan salinan struktur Point dan Anda akan mendapatkan kesalahan yang sama.
Hmm ... setelah membaca pertanyaan Anda dengan lebih hati-hati, mungkin Anda sebenarnya bermaksud memodifikasi variabel dukungan langsung dari dalam kelas Anda: -
Ya itu yang akan Anda butuhkan.
sumber
Sekarang Anda sudah tahu apa sumber kesalahan itu. Jika konstruktor tidak ada dengan kelebihan untuk mengambil properti Anda (dalam hal ini
X
), Anda dapat menggunakan penginisialisasi objek (yang akan melakukan semua keajaiban di belakang layar). Bukan berarti Anda tidak perlu membuat struct Anda abadi , tetapi hanya memberikan info tambahan:Ini dimungkinkan karena di balik layar ini terjadi:
Ini sepertinya hal yang sangat aneh untuk dilakukan, sama sekali tidak direkomendasikan. Hanya mendaftar cara alternatif. Cara yang lebih baik untuk dilakukan adalah membuat struct tidak berubah dan menyediakan konstruktor yang tepat.
sumber
Origin.Y
? Diberikan properti tipePoint
, saya akan berpikir cara idiomatis untuk berubahX
akanvar temp=thing.Origin; temp.X = 23; thing.Origin = temp;
. Pendekatan idiomatik memiliki keuntungan yang tidak harus menyebutkan anggota yang tidak ingin dimodifikasi, fitur yang hanya mungkin karenaPoint
dapat diubah. Saya bingung dengan filosofi yang mengatakan bahwa karena kompiler tidak dapat mengizinkanOrigin.X = 23;
orang harus merancang struct untuk memerlukan kode sepertiOrigin.X = new Point(23, Origin.Y);
. Yang terakhir sepertinya sangat menjengkelkan bagi saya.X
danY
untuk konstruktor tertentu). Sekarang kehilangan titik ketika seseorang bisa melakukannyaPoint p = new Point()
. Saya tahu mengapa itu benar-benar diperlukan untuk struct, jadi tidak ada gunanya berpikir tentang itu. Tetapi apakah Anda memiliki ide keren untuk memperbarui satu properti sajaX
?Object
. Mereka tidak melakukannya. Setiap definisi tipe nilai sebenarnya mendefinisikan dua jenis hal: tipe lokasi penyimpanan (digunakan untuk variabel, slot array, dll.) Dan tipe objek tumpukan, kadang-kadang disebut sebagai tipe "kotak" (digunakan ketika nilai tipe nilai disimpan ke lokasi tipe referensi).Selain memperdebatkan pro dan kontra dari struct versus kelas, saya cenderung melihat tujuan dan mendekati masalah dari perspektif itu.
Yang sedang berkata, jika Anda tidak perlu menulis kode di belakang properti dapatkan dan mengatur metode (seperti dalam contoh Anda), maka tidak akan lebih mudah untuk hanya mendeklarasikan
Origin
sebagai bidang kelas daripada properti? Saya harus berpikir ini akan memungkinkan Anda untuk mencapai tujuan Anda.sumber
Masalahnya adalah Anda menunjuk ke nilai yang terletak di tumpukan dan nilainya tidak akan dikembalikan ke properti orignal sehingga C # tidak memungkinkan Anda mengembalikan referensi ke tipe nilai. Saya pikir Anda dapat menyelesaikan ini dengan menghapus properti Origin dan bukannya menggunakan arsip publik, ya saya tahu itu bukan solusi yang bagus. Solusi lain adalah dengan tidak menggunakan Point, dan sebaliknya membuat tipe Point Anda sendiri sebagai objek.
sumber
Point
adalah anggota dari tipe referensi maka itu tidak akan berada di tumpukan, itu akan berada di tumpukan di memori objek yang mengandung.Saya kira tangkapan di sini adalah bahwa Anda mencoba untuk menetapkan sub-nilai objek dalam pernyataan daripada menetapkan objek itu sendiri. Anda perlu menetapkan seluruh objek Point dalam kasus ini karena tipe properti adalah Point.
Semoga saya masuk akal di sana
sumber
Point
adalah tipe kelas yang bisa diubah, kode asli akan menetapkan bidang atau propertiX
dalam objek yang dikembalikan oleh propertiOrigin
. Saya tidak melihat alasan untuk percaya bahwa akan memiliki efek yang diinginkan pada objek yang mengandungOrigin
properti. Beberapa kelas Framework memiliki properti yang menyalin statusnya ke instance kelas yang dapat diubah dan mengembalikannya. Desain seperti ini memiliki keunggulan memungkinkan kode sukathing1.Origin = thing2.Origin;
mengatur keadaan asal objek agar sesuai dengan yang lain, tetapi tidak bisa memperingatkan tentang kode sepertithing1.Origin.X += 4;
.Hapus saja properti "siapkan" sebagai diikuti, dan kemudian semuanya berfungsi seperti biasa.
Dalam kasus jenis primitif instread gunakan get; set; ...
sumber
Saya pikir banyak orang menjadi bingung di sini, masalah khusus ini berkaitan dengan pemahaman bahwa properti tipe nilai mengembalikan salinan tipe nilai (seperti dengan metode dan pengindeks), dan bidang tipe nilai diakses secara langsung . Kode berikut melakukan persis apa yang Anda coba capai dengan mengakses bidang dukungan properti secara langsung (catatan: mengekspresikan properti dalam bentuk verbose dengan bidang dukungan adalah setara dengan properti otomatis, tetapi memiliki keuntungan bahwa dalam kode kami, kami dapat mengakses bidang dukungan secara langsung):
Kesalahan yang Anda dapatkan adalah konsekuensi tidak langsung dari tidak memahami bahwa properti mengembalikan salinan tipe nilai. Jika Anda mengembalikan salinan tipe nilai dan Anda tidak menetapkannya ke variabel lokal maka setiap perubahan yang Anda buat pada salinan itu tidak akan pernah bisa dibaca dan oleh karena itu kompiler meningkatkan ini sebagai kesalahan karena ini tidak bisa disengaja. Jika kita menetapkan salinan ke variabel lokal maka kita dapat mengubah nilai X, tetapi itu hanya akan diubah pada salinan lokal, yang memperbaiki kesalahan waktu kompilasi, tetapi tidak akan memiliki efek yang diinginkan dari memodifikasi properti Origin. Kode berikut menggambarkan ini, karena kesalahan kompilasi hilang, tetapi pernyataan debug akan gagal:
sumber