Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
16.1 Umum
Struktur mirip dengan kelas di mana mereka mewakili struktur data yang dapat berisi anggota data dan anggota fungsi. Namun, tidak seperti kelas, struktur adalah jenis nilai dan tidak memerlukan alokasi tumpukan. Variabel dari jenis struct secara langsung berisi data struct, sedangkan variabel dari tipe kelas berisi referensi ke data tersebut, yang terakhir dikenal sebagai objek.
Catatan: Struktur sangat berguna untuk struktur data kecil yang memiliki semantik nilai. Bilangan kompleks, titik dalam sistem koordinat, atau pasangan kunci-nilai dalam kamus adalah semua contoh struktur yang baik. Kunci dari struktur data ini adalah bahwa mereka memiliki sedikit anggota data, mereka tidak memerlukan penggunaan pewarisan atau semantik referensi, melainkan dapat dengan mudah diimplementasikan menggunakan semantik nilai di mana penugasan menyalin nilai alih-alih referensi. catatan akhir
Seperti yang dijelaskan dalam §8.3.5, jenis sederhana yang disediakan oleh C#, seperti int, double, dan bool, semuanya adalah jenis struct.
16.2 Deklarasi struktur
16.2.1 Umum
struct_declaration adalah type_declaration (§14,7) yang menyatakan struktur baru:
struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
struct_declaration terdiri dari sekumpulan atribut opsional (§23), diikuti dengan sekumpulan struct_modifieropsional (§16.2.2), diikuti dengan modif opsional ref (§16.2.3), diikuti oleh pengubah parsial opsional (§15.2.7), diikuti oleh kata kunci struct dan pengidentifikasi yang menamai struktur, diikuti dengan spesifikasi type_parameter_list opsional (§15.2.3), diikuti oleh opsional spesifikasi struct_interfaces (§16.2.5), diikuti dengan spesifikasi type_parameter_constraints-klausa opsional (§15.2.5), diikuti oleh struct_body (§16.2.6), secara opsional diikuti oleh titik koma.
Deklarasi struktur tidak harus menyediakan type_parameter_constraints_clausekecuali juga menyertakan type_parameter_list.
Deklarasi struktur yang memasok type_parameter_list adalah deklarasi struktur generik. Selain itu, setiap struktur yang bersarang di dalam deklarasi kelas generik atau deklarasi struct generik adalah deklarasi struktur generik, karena argumen jenis untuk jenis yang berisi harus disediakan untuk membuat jenis yang dibangun (§8.4).
Deklarasi struktur yang menyertakan ref kata kunci tidak boleh memiliki bagian struct_interfaces .
16.2.2 Pengubah struktur
Pernyataan struktur dapat mencakup urutan pengubah struktur jika diinginkan.
struct_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| 'readonly'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) hanya tersedia dalam kode tidak aman (§24).
Ini adalah kesalahan waktu kompilasi jika pengubah yang sama muncul beberapa kali dalam deklarasi struct.
Kecuali untuk readonly, pengubah deklarasi struct memiliki arti yang sama dengan deklarasi kelas (§15.2.2).
Pengubah readonly menunjukkan bahwa struct_declaration mendeklarasikan jenis yang instansnya tidak dapat diubah.
Struktur baca-saja memiliki batasan berikut:
- Setiap bidang instansnya juga harus dinyatakan
readonly. - Ini tidak boleh menyatakan peristiwa seperti bidang (§15.8.2).
Ketika instans struktur baca-saja diteruskan ke metode , instans this tersebut diperlakukan seperti argumen/parameter input, yang melarang akses tulis ke bidang instans apa pun (kecuali oleh konstruktor).
16.2.3 Pengubah referensi
Pengubah ref menunjukkan bahwa struct_declaration mendeklarasikan jenis yang instansnya dialokasikan pada tumpukan eksekusi. Jenis ini disebut ref struct. Pengubah ref menyatakan bahwa instans mungkin berisi bidang seperti ref, dan tidak boleh disalin dari konteks amannya (§16.4.15). Aturan untuk menentukan konteks aman dari struktur ref dijelaskan dalam §16.4.15.
Ini adalah kesalahan waktu kompilasi jika jenis struct ref digunakan dalam salah satu konteks berikut:
- Sebagai tipe elemen array.
- Sebagai jenis bidang kelas yang dideklarasikan atau struktur yang tidak memiliki
refpengubah. - Dibatasi ke
System.ValueTypeatauSystem.Object. - Sebagai argumen jenis.
- Sebagai jenis elemen tuple.
- Metode asinkron.
- Sebuah iterator.
- Tidak ada konversi dari
ref structjenis ke jenisobjectatau jenisSystem.ValueType. - Jenis
ref structtidak boleh dinyatakan untuk mengimplementasikan antarmuka apa pun. - Metode instans yang dideklarasikan pada
objectatau padaSystem.ValueType, tetapi tidak ditimpa pada jenisref struct, tidak boleh dipanggil dengan penerima dari jenisref structtersebut. - Metode instance dari tipe
ref structtidak boleh ditangkap melalui konversi grup metode ke tipe delegasi. - Struktur ref tidak boleh ditangkap oleh ekspresi lambda atau fungsi lokal.
Catatan: Sebuah
ref structtidak boleh mendeklarasikan metode instanceasyncatau menggunakan pernyataanyield returnatauyield breakdalam metode instance, karena parameter implisitthistidak dapat digunakan dalam konteks tersebut. catatan akhir
Batasan ini memastikan bahwa variabel jenis ref struct tidak merujuk ke memori tumpukan yang tidak lagi valid, atau ke variabel yang tidak lagi valid.
16.2.4 Pengubah parsial
Pengubah partial menunjukkan bahwa struct_declaration ini adalah deklarasi jenis parsial. Beberapa deklarasi struct parsial dengan nama yang sama dalam namespace atau deklarasi tipe yang mencakup digabungkan untuk membentuk satu deklarasi struct, mengikuti aturan yang ditentukan dalam §15.2.7.
16.2.5 Antarmuka struktur
Deklarasi struktur dapat mencakup spesifikasi struct_interfaces , dalam hal ini struktur dikatakan untuk secara langsung mengimplementasikan jenis antarmuka yang diberikan. Untuk tipe struct yang dibangun, termasuk tipe bersarang yang dideklarasikan dalam deklarasi tipe generik (§15.3.9.7), setiap tipe antarmuka yang diimplementasikan diperoleh dengan menggantikan, untuk setiap type_parameter dalam antarmuka tertentu, type_argument yang sesuai dari tipe yang dibangun.
struct_interfaces
: ':' interface_type_list
;
Penanganan antarmuka pada beberapa bagian dari deklarasi struktur parsial (§15.2.7) dibahas lebih lanjut dalam §15.2.4.3.
Implementasi antarmuka dibahas lebih lanjut dalam §19.6.
16.2.6 Badan Struktur
struct_body dari sebuah struktur mendefinisikan anggota-anggota struktur.
struct_body
: '{' struct_member_declaration* '}'
;
16.3 Anggota struktur
Anggota struktur terdiri dari anggota yang diperkenalkan oleh struct_member_declaration dan anggota yang diwarisi dari tipe System.ValueType.
struct_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| operator_declaration
| constructor_declaration
| static_constructor_declaration
| type_declaration
| fixed_size_buffer_declaration // unsafe code support
;
fixed_size_buffer_declaration (§24.8.2) hanya tersedia dalam kode tidak aman (§24).
Catatan: Semua jenis class_member_declaration kecuali finalizer_declaration juga struct_member_declaration. catatan akhir
Kecuali untuk perbedaan yang disebutkan dalam §16.4, deskripsi anggota kelas yang disediakan dalam §15.3 hingga §15.12 berlaku untuk anggota struktur juga.
16.4 Perbedaan kelas dan struktur
16.4.1 Umum
Struktur berbeda dari kelas dengan beberapa cara penting:
- Struktur adalah jenis nilai (§16.4.2).
- Semua jenis struct secara implisit mewarisi dari kelas
System.ValueType(§16.4.3). - Penugasan ke variabel jenis struct membuat salinan dari nilai yang ditetapkan (§16.4.4).
- Nilai default struct adalah nilai yang dihasilkan dengan mengatur semua bidang ke nilai defaultnya (§16.4.5).
- Operasi boxing dan unboxing digunakan untuk mengonversi antara jenis struct dan jenis referensi tertentu (§16.4.6).
- Arti dari
thisberbeda dalam anggota struktur (§16.4.7). - Deklarasi bidang instans untuk struktur tidak diizinkan untuk menyertakan penginisialisasi variabel (§16.4.8).
- Struktur tidak diizinkan untuk mendeklarasikan konstruktor instans tanpa parameter (§16.4.9).
- Struct tidak diizinkan untuk mendeklarasikan finalizer.
- Deklarasi event, deklarasi properti, pengakses properti, deklarasi pengindeks, dan deklarasi metode diizinkan untuk memiliki pengubah
readonlymeskipun hal tersebut umumnya tidak diizinkan untuk jenis anggota yang sama di dalam kelas.
16.4.2 Nilai semantik
Struktur adalah jenis nilai (§8,3) dan dikatakan memiliki semantik nilai. Kelas, di sisi lain, adalah jenis referensi (§8.2) dan dikatakan memiliki semantik referensi.
Variabel jenis struct secara langsung berisi data struct, sedangkan variabel jenis kelas berisi referensi ke objek yang berisi data. Ketika struct B berisi bidang instans jenis A dan A merupakan jenis struct, itu adalah kesalahan pada waktu kompilasi untuk A bergantung pada B atau jenis yang dibangun dari B. Struktur Xbergantung langsung pada strukturY jika X berisi bidang instans jenis Y. Mengingat definisi ini, himpunan lengkap dari struktur yang diandalkan oleh suatu struktur adalah penutupan transitif hubungan langsung tergantung pada.
Contoh:
struct Node { int data; Node next; // error, Node directly depends on itself }adalah kesalahan karena
Nodeberisi bidang instans dengan jenisnya sendiri. Contoh lainstruct A { B b; } struct B { C c; } struct C { A a; }adalah kesalahan karena masing-masing jenis
A,B, danCbergantung satu sama lain.contoh akhir
Dengan kelas, dimungkinkan bagi dua variabel untuk mereferensikan objek yang sama, dan dengan demikian mungkin untuk operasi pada satu variabel untuk memengaruhi objek yang dirujuk oleh variabel lain. Dengan struct, variabel masing-masing memiliki salinan data mereka sendiri (kecuali dalam kasus parameter by-reference), dan tidak dimungkinkan bagi operasi pada satu untuk memengaruhi yang lain. Selain itu, kecuali ketika secara eksplisit nullable (§8.3.12), nilai dari tipe struct tidak mungkin menjadi bernilai null.
Catatan: Jika struct berisi bidang jenis referensi, konten objek yang direferensikan dapat diubah oleh operasi lain. Namun nilai bidang itu sendiri, yaitu, objek mana yang dirujuknya, tidak dapat diubah melalui mutasi nilai struct yang berbeda. catatan akhir
Contoh: Diberikan sebagai berikut
struct Point { public int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point a = new Point(10, 10); Point b = a; a.x = 100; Console.WriteLine(b.x); } }outputnya adalah
10. Penugasanauntukbmembuat salinan nilai, danbdengan demikian tidak terpengaruh oleh penugasan kea.x. SebaliknyaPointtelah dinyatakan sebagai kelas, outputnya adalah100karenaadanbakan mereferensikan objek yang sama.contoh akhir
16.4.3 Warisan
Semua jenis struct secara implisit mewarisi dari kelas System.ValueType, yang, pada gilirannya, mewarisi dari kelas object. Deklarasi struktur dapat menentukan daftar antarmuka yang diimplementasikan, tetapi tidak dimungkinkan untuk deklarasi struktur untuk menentukan kelas dasar.
Jenis struktur tidak pernah abstrak dan selalu disegel secara implisit. Oleh karena itu, pengubah abstract dan sealed tidak diizinkan dalam deklarasi struktur.
Karena pewarisan tidak didukung untuk struktur, aksesibilitas yang dinyatakan dari anggota struct tidak boleh protected, private protected, atau protected internal.
Anggota fungsi dalam struktur tidak boleh abstrak atau virtual, dan override pengubah hanya diizinkan untuk mengambil alih metode yang diwariskan dari System.ValueType.
16.4.4 Penugasan
Penugasan nilai ke variabel dengan tipe struct membuat salinan dari nilai yang ditugaskan. Ini berbeda dari penugasan ke variabel jenis kelas, yang menyalin referensi tetapi bukan objek yang diidentifikasi oleh referensi.
Mirip dengan penugasan, ketika struct diberikan sebagai parameter nilai atau dikembalikan sebagai hasil dari sebuah fungsi, salinan struct dibuat. Struktur dapat diteruskan melalui referensi ke anggota fungsi dengan parameter by-reference.
Ketika properti atau pengindeks struct adalah target penugasan, ekspresi instans yang terkait dengan properti atau akses pengindeks harus diklasifikasikan sebagai variabel. Jika ekspresi instans diklasifikasikan sebagai nilai, akan terjadi kesalahan saat kompilasi. Ini dijelaskan secara lebih rinci dalam §12.23.2.
16.4.5 Nilai default
Seperti yang dijelaskan dalam §9.3, beberapa jenis variabel secara otomatis diinisialisasi ke nilai default mereka ketika dibuat. Untuk variabel jenis kelas dan jenis referensi lainnya, nilai default ini adalah null. Namun, karena struktur adalah jenis nilai yang tidak boleh null, nilai default struct adalah nilai yang dihasilkan dengan mengatur semua bidang jenis nilai ke nilai defaultnya dan semua bidang jenis referensi ke null.
Contoh: Mengacu pada struktur yang
Pointdinyatakan di atas, contohPoint[] a = new Point[100];menginisialisasi setiap
Pointdalam array ke nilai yang dihasilkan dengan cara mengatur bidangxdanymenjadi nol.contoh akhir
Nilai default struct sesuai dengan nilai yang dikembalikan oleh konstruktor default struct (§8.3.3). Tidak seperti kelas, struct tidak diizinkan untuk mendeklarasikan konstruktor instans tanpa parameter. Sebaliknya, setiap struktur secara implisit memiliki konstruktor instans tanpa parameter, yang selalu mengembalikan nilai yang dihasilkan dari pengaturan semua bidang ke nilai defaultnya.
Catatan: Struktur harus dirancang untuk mempertimbangkan status inisialisasi default sebagai status yang valid. Dalam contoh
struct KeyValuePair { string key; string value; public KeyValuePair(string key, string value) { if (key == null || value == null) { throw new ArgumentException(); } this.key = key; this.value = value; } }konstruktor instance yang didefinisikan pengguna hanya melindungi dari nilai
nulljika dipanggil secara eksplisit. Dalam kasus di mana variabelKeyValuePairtunduk pada inisialisasi nilai default, bidangkeydanvalueakan berada dalam kondisinull, dan struktur harus dipersiapkan untuk menangani kondisi ini.catatan akhir
16.4.6 Tinju dan pembukaan kotak
Nilai jenis kelas dapat dikonversi ke jenis object atau ke jenis antarmuka yang diimplementasikan oleh kelas hanya dengan memperlakukan referensi sebagai jenis lain pada waktu kompilasi. Demikian juga, nilai jenis object atau nilai jenis antarmuka dapat dikonversi kembali ke jenis kelas tanpa mengubah referensi (tetapi, tentu saja, pemeriksaan jenis run-time diperlukan dalam kasus ini).
Karena struktur bukan jenis referensi, operasi ini diimplementasikan secara berbeda untuk jenis struct. Ketika nilai dari jenis struct dikonversi ke jenis referensi tertentu (seperti yang didefinisikan dalam §10.2.9), operasi pembungkusan terjadi. Demikian juga, ketika nilai jenis referensi tertentu (seperti yang didefinisikan dalam §10.3.7) dikonversi kembali ke jenis struct, operasi unboxing terjadi. Perbedaan utama dari operasi yang sama pada tipe kelas adalah bahwa boxing dan unboxing menyalin nilai struct ke dalam atau mengeluarkannya dari instance yang dibungkus.
Catatan: Dengan demikian, setelah melakukan operasi boxing atau unboxing, perubahan yang dilakukan pada objek yang telah di-unbox tidak tercermin pada objek yang telah di-box. catatan akhir
Untuk detail lebih lanjut tentang boxing dan unboxing, lihat §10.2.9 dan §10.3.7.
16.4.7 Arti dari ini
Arti this dalam struktur berbeda dari arti this dalam kelas, seperti yang dijelaskan dalam §12.8.14. Ketika tipe struct menggantikan metode virtual yang diwarisi dari System.ValueType (seperti Equals, GetHashCode, atau ToString), pemanggilan metode virtual melalui instance dari tipe struct tidak menyebabkan boxing terjadi. Ini berlaku bahkan ketika struct digunakan sebagai parameter tipe dan pemanggilan terjadi melalui sebuah instans dari tipe parameter tersebut.
Contoh:
struct Counter { int value; public override string ToString() { value++; return value.ToString(); } } class Program { static void Test<T>() where T : new() { T x = new T(); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); Console.WriteLine(x.ToString()); } static void Main() => Test<Counter>(); }Output dari program adalah:
1 2 3Meskipun gaya yang buruk untuk
ToStringmemiliki efek samping, contoh menunjukkan bahwa tidak ada tinju yang terjadi untuk tiga pemanggilanx.ToString().contoh akhir
Demikian pula, tinju tidak pernah secara implisit terjadi saat mengakses anggota pada parameter jenis yang dibatasi ketika anggota diimplementasikan dalam jenis nilai. Misalnya, antarmuka ICounter berisi metode Increment, yang dapat digunakan untuk memodifikasi nilai. Jika ICounter digunakan sebagai batasan, implementasi dari metode Increment dipanggil dengan referensi ke variabel tempat Increment dipanggil, bukan salinan yang dibungkus.
Contoh:
interface ICounter { void Increment(); } struct Counter : ICounter { int value; public override string ToString() => value.ToString(); void ICounter.Increment() => value++; } class Program { static void Test<T>() where T : ICounter, new() { T x = new T(); Console.WriteLine(x); x.Increment(); // Modify x Console.WriteLine(x); ((ICounter)x).Increment(); // Modify boxed copy of x Console.WriteLine(x); } static void Main() => Test<Counter>(); }Panggilan pertama untuk
Incrementmemodifikasi nilai dalam variabelx. Ini tidak setara dengan panggilan kedua keIncrement, yang memodifikasi nilai dalam salinan yang terkotak darix. Dengan demikian, output program adalah:0 1 1contoh akhir
16.4.8 Penginisialisasi bidang
Seperti yang dijelaskan dalam §16.4.5, nilai default struct terdiri dari nilai yang dihasilkan dari mengatur semua bidang jenis nilai ke nilai defaultnya dan semua bidang jenis referensi ke null. Untuk alasan ini, struct tidak mengizinkan deklarasi field instance untuk menyertakan penginisialisasi variabel. Pembatasan ini hanya berlaku untuk bidang instans. Bidang statis dari struct diizinkan untuk menyertakan penginisialisasi variabel.
Contoh: Berikut ini
struct Point { public int x = 1; // Error, initializer not permitted public int y = 1; // Error, initializer not permitted }terjadi kesalahan karena deklarasi bidang instans menyertakan penginisialisasi variabel.
contoh akhir
field_declaration yang dinyatakan langsung di dalam struct_declaration memiliki struct_modifierreadonly akan memiliki field_modifierreadonly.
16.4.9 Konstruktor
Tidak seperti kelas, struct tidak diizinkan untuk mendeklarasikan konstruktor instans tanpa parameter. Sebaliknya, setiap struktur secara implisit memiliki konstruktor instans tanpa parameter, yang selalu mengembalikan nilai yang dihasilkan dari pengaturan semua bidang jenis nilai ke nilai defaultnya dan semua bidang jenis referensi ke null (§8.3.3). Struct dapat mendeklarasikan konstruktor instans yang memiliki parameter.
Contoh: Diberikan sebagai berikut
struct Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } } class A { static void Main() { Point p1 = new Point(); Point p2 = new Point(0, 0); } }pernyataan keduanya membuat
Pointdenganxdanydiinisialisasi ke nol.contoh akhir
Konstruktor instans struct tidak diizinkan untuk menyertakan inisialisasi konstruktor formulir base(argument_list), di mana argument_list bersifat opsional.
Parameter this dari konstruktor instance pada struct berkaitan dengan parameter keluaran dari tipe struct. Dengan demikian, this pasti akan ditetapkan (§9.4) di setiap lokasi tempat konstruktor kembali. Demikian pula, itu tidak dapat dibaca (bahkan secara implisit) dalam tubuh konstruktor sebelum diberi nilai.
Jika konstruktor instans struct menentukan penginisialisasi konstruktor, inisialisasi tersebut dianggap sebagai penugasan pasti untuk ini yang terjadi sebelum isi konstruktor. Oleh karena itu, tubuh itu sendiri tidak memiliki persyaratan inisialisasi.
Contoh: Pertimbangkan implementasi konstruktor instans di bawah ini:
struct Point { int x, y; public int X { set { x = value; } } public int Y { set { y = value; } } public Point(int x, int y) { X = x; // error, this is not yet definitely assigned Y = y; // error, this is not yet definitely assigned } }Tidak ada anggota fungsi instans (termasuk aksesor yang ditetapkan untuk properti
XdanY) yang dapat dipanggil sampai semua bidang struktur yang dibangun telah pasti ditetapkan. Namun, perhatikan bahwa jikaPointmerupakan kelas alih-alih struct, implementasi konstruktor instans akan diizinkan. Ada satu pengecualian untuk ini, dan yang melibatkan properti yang diterapkan secara otomatis (§15.7.4). Aturan penetapan yang pasti (§12.23.2) secara khusus mengecualikan penugasan ke properti otomatis dari jenis struct dalam konstruktor instans dari jenis struct tersebut: penugasan tersebut dianggap sebagai penugasan pasti dari bidang pencadangan tersembunyi dari properti otomatis. Dengan demikian, berikut ini diizinkan:struct Point { public int X { get; set; } public int Y { get; set; } public Point(int x, int y) { X = x; // allowed, definitely assigns backing field Y = y; // allowed, definitely assigns backing field } }akhiri contoh]
16.4.10 Konstruktor statis
Konstruktor statis untuk struktur mengikuti sebagian besar aturan yang sama seperti untuk kelas. Pelaksanaan konstruktor statis untuk tipe struct dipicu oleh salah satu dari peristiwa berikut yang pertama kali terjadi dalam domain aplikasi:
- Anggota statis dari tipe struct sedang dirujuk.
- Konstruktor yang dinyatakan secara eksplisit dari jenis struct dipanggil.
Catatan: Pembuatan nilai default (§16.4.5) dari jenis struct tidak memicu konstruktor statis. (Contohnya adalah nilai awal elemen dalam array.) catatan akhir
16.4.11 Properti
Property_declaration (§15.7.1) untuk properti instans dalam struct_declaration mungkin berisi property_modifierreadonly. Namun, properti statis tidak boleh berisi pengubah tersebut.
Ini adalah kesalahan waktu kompilasi untuk mencoba memodifikasi status variabel struct instans melalui properti readonly yang dideklarasikan dalam struktur tersebut.
Ini adalah kesalahan waktu kompilasi untuk properti yang diimplementasikan secara otomatis yang memiliki readonly pengubah, untuk juga memiliki set aksesor.
Ini adalah kesalahan waktu kompilasi untuk properti yang diimplementasikan secara otomatis dalam readonly struktur untuk memiliki set aksesor.
Properti yang diimplementasikan secara otomatis yang dinyatakan di dalam readonly struktur tidak perlu memiliki readonly pengubah, karena pengaksesnya get secara implisit diasumsikan sebagai readonly.
Ini adalah kesalahan waktu kompilasi jika terdapat readonly pengubah pada properti itu sendiri maupun pada aksesor get dan set.
Ini adalah kesalahan waktu kompilasi jika properti memiliki modifier read-only pada semua aksesornya.
Catatan: Untuk memperbaiki kesalahan, pindahkan pengubah dari aksesor ke properti itu sendiri. catatan akhir
Untuk ekspresi aksesor properti, s.P:
- Ini adalah kesalahan waktu kompilasi jika
s.Pmemanggil aksesorMset jenisTketika proses di §12.6.6.1 akan membuat salinan sementara .s - Jika
s.Pmemanggil aksesor get jenisT, proses di §12.6.6.1 diikuti, termasuk membuat salinanssementara jika diperlukan.
Properti yang diimplementasikan secara otomatis (§15.7.4) menggunakan bidang backing tersembunyi, yang hanya dapat diakses oleh pengakses properti.
Catatan: Pembatasan akses ini berarti bahwa konstruktor dalam struktur yang mengandung properti yang diimplementasikan secara otomatis sering kali memerlukan inisialisasi konstruktor eksplisit, meskipun biasanya tidak dibutuhkan, untuk memenuhi persyaratan bahwa semua bidang harus ditetapkan secara pasti sebelum anggota fungsi dipanggil atau konstruktor kembali. catatan akhir
16.4.12 Metode
method_declaration (§15.6.1) untuk metode instans dalam struct_declaration mungkin berisi method_modifierreadonly. Namun, metode statis tidak boleh berisi pengubah tersebut.
Ini adalah kesalahan pada saat waktu kompilasi untuk mencoba memodifikasi status variabel struct instans melalui metode readonly yang dideklarasikan dalam struct tersebut.
Meskipun metode readonly dapat memanggil metode non-readonly dari kelas sejenis, atau properti atau pengindeks dengan aksesor get, tindakan ini menghasilkan pembuatan salinan this implisit sebagai langkah perlindungan.
Metode baca-saja dapat memanggil properti saudara atau aksesor pengaturan pengindeks yang baca-saja. Jika anggota saudara memiliki aksesor yang tidak eksplisit atau implisit readonly, maka akan terjadi kesalahan kompilasi.
Semua deklarasi metode parsial harus memiliki readonly pengubah, atau tidak satu pun dari mereka boleh memilikinya.
16.4.13 Pengindeks
indexer_declaration (§15,9) untuk pengindeks instans dalam struct_declaration mungkin berisi indexer_modifierreadonly.
Ini adalah kesalahan waktu kompilasi untuk mencoba memodifikasi status variabel struct instans melalui pengindeks baca-saja yang dideklarasikan dalam struktur tersebut.
Ini adalah kesalahan waktu kompilasi untuk memiliki pengubah readonly pada pengindeks itu sendiri maupun pada salah satu pengaksesnya, yaitu get atau set.
Kesalahan waktu kompilasi terjadi ketika indeks memiliki modifier hanya-baca pada semua pengaksesnya.
Catatan: Untuk memperbaiki kesalahan, pindahkan pengubah dari pengakses ke pengindeks itu sendiri. catatan akhir
16.4.14 Peristiwa
Event_declaration (§15.8.1) untuk peristiwa nonfield-like pada instans dalam struct_declaration mungkin berisi event_modifierreadonly. Namun, peristiwa statis tidak boleh berisi pengubah tersebut.
16.4.15 Batasan konteks aman
16.4.15.1 Umum
Pada waktu kompilasi, setiap ekspresi dikaitkan dengan konteks di mana instans tersebut dan semua bidangnya dapat diakses dengan aman, konteksnya yang aman. Konteks aman adalah konteks yang mencakup ekspresi, di mana nilai dapat tersebar dengan aman.
Ekspresi apa pun yang jenis waktu kompilasinya bukan struktur ref memiliki konteks pemanggil yang aman.
Ekspresi default, untuk jenis apa pun, memiliki konteks pemanggil yang aman.
Untuk ekspresi non-default yang jenis waktu kompilasinya adalah struktur ref memiliki konteks aman yang ditentukan oleh bagian berikut.
Konteks aman mencatat konteks mana nilai dapat disalin. Diberikan penugasan dari ekspresi E1 dengan konteks aman S1, menuju ekspresi E2 dengan konteks aman S2, adalah kesalahan jika S2 memiliki konteks yang lebih luas daripada S1.
Ada tiga nilai konteks aman yang berbeda, sama dengan nilai konteks aman-ref yang ditentukan untuk variabel referensi (§9.7.2): blok-deklarasi, anggota-fungsi, dan konteks-pemanggil. Konteks aman ekspresi membatasi penggunaannya sebagai berikut:
- Untuk pernyataan pengembalian
return e1, konteks amane1harus berupa konteks pemanggil. - Untuk penugasan
e1 = e2, konteks aman darie2harus setidaknya seluas konteks aman darie1.
Jika dalam pemanggilan metode terdapat argumen ref atau out dari jenis ref struct (termasuk penerima kecuali jenisnya readonly) dengan konteks aman S1, maka tidak ada argumen (termasuk penerima) yang boleh memiliki konteks aman yang lebih sempit daripada S1.
16.4.15.2 Parameter dalam konteks yang aman
Parameter dari jenis struct ref, termasuk parameter this dari metode instans, memiliki konteks pemanggil yang aman.
Konteks aman untuk variabel lokal
Variabel lokal dari jenis struct ref memiliki konteks aman sebagai berikut:
- Jika variabel adalah variabel perulangan dari perulangan
foreach, maka konteks aman variabel sama dengan konteks aman ekspresiforeachperulangan. - Jika tidak, jika deklarasi variabel memiliki penginisialisasi, maka konteks aman variabel sama dengan konteks aman penginisialisasi tersebut.
- Jika tidak, variabel tersebut tidak diinisialisasi pada saat deklarasi dan memiliki konteks pemanggil yang aman.
16.4.15.4 Konteks aman bidang
Referensi ke bidang e.F, di mana jenis F adalah jenis struct ref, memiliki konteks aman yang sama dengan konteks e.
16.4.15.5 Operator
Aplikasi operator yang ditentukan pengguna diperlakukan sebagai pemanggilan metode (§16.4.15.6).
Untuk operator yang menghasilkan nilai, seperti e1 + e2 atau c ? e1 : e2, konteks aman hasil adalah konteks tersempit di antara konteks aman dari operand operator. Sebagai konsekuensinya, untuk operator unary yang menghasilkan nilai, seperti +e, konteks aman dari hasil adalah konteks operand yang aman.
Catatan: Operand pertama dari operator bersyarah adalah
bool, sehingga konteks amannya adalah konteks pemanggil. Dengan demikian, konteks aman yang dihasilkan adalah konteks aman tersempit dari operand kedua dan ketiga. catatan akhir
16.4.15.6 Metode dan pemanggilan properti
Nilai yang dihasilkan dari pemanggilan metode e1.M(e2, ...) atau pemanggilan properti e.P memiliki konteks teraman dari konteks terkecil berikut:
- konteks pemanggil
- Konteks aman dari semua ekspresi argumen (termasuk penerima).
Pemanggilan properti (baik get atau set) diperlakukan sebagai pemanggilan terhadap metode yang mendasarinya berdasarkan aturan di atas.
16.4.15.7 stackalloc
Hasil ekspresi stackalloc memiliki konteks aman dari anggota fungsi.
16.4.15.8 Pemanggilan konstruktor
Ekspresi new yang memanggil konstruktor mengikuti aturan yang sama dengan pemanggilan metode yang dianggap mengembalikan tipe yang sedang dibangun.
Selain itu, konteks aman adalah yang terkecil dari konteks aman untuk semua argumen dan operand dalam semua ekspresi penginisialisasi objek, secara rekursif, jika ada penginisialisasi.
Catatan: Aturan ini mengandalkan
Span<T>tidak memiliki konstruktor formulir berikut:public Span<T>(ref T p)Konstruktor semacam itu membuat instans
Span<T>yang digunakan sebagai bidang tidak dapat dibedakan dari bidangref. Aturan keamanan yang dijelaskan dalam dokumen ini bergantung padarefbidang yang tidak menjadi konstruksi yang valid di C# atau .NET. catatan akhir
ECMA C# draft specification