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,8) yang menyatakan struktur baru:
struct_declaration
: non_record_struct_declaration
| record_struct_declaration
;
non_record_struct_declaration
: attributes? struct_modifier* 'ref'? 'partial'? 'struct'
identifier type_parameter_list? struct_interfaces?
type_parameter_constraints_clause* struct_body ';'?
;
record_struct_declaration
: attributes? struct_modifier* 'partial'? 'record' 'struct'
identifier type_parameter_list? delimited_parameter_list? struct_interfaces?
type_parameter_constraints_clause* record_struct_body
;
record_struct_body
: struct_body ';'?
| ';'
;
struct_declaration adalah untuk struktur non-rekaman atau struktur rekaman.
non_record_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 dengan spesifikasi struct_interfaces opsional (§16.2.5), diikuti dengan spesifikasi type_parameter_constraints-klausa opsional (§15.2.5), diikuti oleh struct_body (§16.2.6), secara opsional diikuti dengan titik koma.
record_struct_declaration terdiri dari sekumpulan atribut opsional (§23), diikuti dengan sekumpulan struct_modifieropsional (§16.2.2), diikuti oleh pengubah parsial opsional (§15.2.7), diikuti dengan kata kunci record, diikuti oleh kata kunci struct dan pengidentifikasi yang menamai struktur, diikuti dengan spesifikasi type_parameter_list opsional (§15.2.3), diikuti oleh delimited_parameter_list opsional spesifikasi (§15.2.1), diikuti dengan spesifikasi struct_interfaces opsional (§16.2.5), diikuti dengan spesifikasi type_parameter_constraints-klausul opsional (§15.2.5), diikuti oleh record_struct_body.
struct_declaration tidak boleh menyediakan type_parameter_constraints_clausekecuali juga memasok type_parameter_list.
Struct_declaration 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).
Non_record_struct_declaration yang mencakup ref pengubah tidak boleh memiliki bagian struct_interfaces.
record_struct_declaration memiliki delimited_parameter_list mendeklarasikan struktur rekaman posisional.
Paling banyak hanya satu record_struct_declaration yang berisi partial yang dapat menyediakan delimited_parameter_list.
Parameter dalam delimited_parameter_list tidak boleh memiliki ref, out atau this pengubah; namun, in dan params pengubah diizinkan.
Untuk record_struct_declaration, record_struct_body{} , {};, dan ; setara. Mereka semua menunjukkan bahwa satu-satunya anggota adalah anggota yang disintesis oleh pengkompilasi (§16.4).
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 non_record_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.5.15). Aturan untuk menentukan konteks aman dari struktur ref dijelaskan dalam §16.5.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. - Sebagai argumen jenis.
- Sebagai jenis elemen tuple.
- Dalam metode asinkron.
- Dalam iterator.
- Sebagai jenis penerima untuk konversi grup metode dari metode instans ke jenis delegasi.
- Sebagai variabel yang diambil dalam ekspresi lambda atau fungsi lokal.
Selain itu, pembatasan berikut berlaku untuk ref struct jenis:
- Jenis
ref structtidak boleh dikotak keSystem.ValueTypeatauSystem.Object. - 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.
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
16.3.1 Umum
Anggota struktur terdiri dari anggota yang diperkenalkan oleh struct_member_declaration dan anggota yang diwarisi dari tipe System.ValueType. Untuk struktur catatan, kumpulan anggota juga menyertakan anggota yang disintesis yang dihasilkan oleh pengkompilasi (§synth-members).
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.5, deskripsi anggota kelas yang disediakan dalam §15.3 hingga §15.12 berlaku untuk anggota struktur juga.
Ini adalah kesalahan untuk bidang instans dari struktur rekaman agar memiliki jenis yang tidak aman.
16.3.2 Anggota readonly
Definisi anggota instans atau aksesor properti instans, pengindeks, atau peristiwa yang menyertakan pengubah readonly memiliki batasan berikut:
- Parameter
thisadalahref readonlyreferensi. - Anggota tidak boleh menetapkan ulang nilai
thisatau bidang instans penerima. - Anggota tidak boleh menetapkan ulang nilai peristiwa seperti bidang instans (§15.8.2) penerima.
- Jika anggota readonly memanggil anggota non-readonly, struktur yang dirujuk oleh
thisharus disalin untuk menggunakan referensi bisa-tulis untuk argumen.this
Catatan: Bidang instans mencakup bidang backing tersembunyi yang digunakan untuk properti yang diterapkan secara otomatis (§15.7.4). catatan akhir
Contoh: Anggota readonly dapat memodifikasi status objek yang dirujuk oleh bidang instans, meskipun anggota readonly tidak dapat menetapkan ulang anggota instans tersebut. Kode berikut menunjukkan penetapan ulang dan memodifikasi bidang instans:
public struct S { private List<string> messages; public S(IEnumerable<string> messages) => this.messages = new List<string>(messages); public void InitializeMessages() => messages = new List<string>(); public readonly void AddMessage(string message) { if (messages == null) { throw new InvalidOperationException("Messages collection is not initialized."); } messages.Add(message); } }Metode
readonlyAddMessageini dapat mengubah status daftar pesan. AnggotaInitializeMessagesdapat menghapus dan menginisialisasi ulang daftar pesan. Dalam kasusAddMessage, pengubahreadonlyvalid. Dalam kasusInitializeMessages, menambahkan pengubahreadonlytidak valid. contoh akhir
16.4 Anggota struktur rekaman yang disintesis
16.4.1 Umum
Dalam kasus struct rekaman, anggota disintesis kecuali anggota dengan tanda tangan "cocok" dinyatakan dalam record_struct_body atau anggota non-virtual konkret yang dapat diakses dengan tanda tangan "cocok" diwarisi. Dua anggota dianggap cocok jika mereka memiliki tanda tangan yang sama atau akan dianggap "bersembunyi" dalam skenario warisan. (Lihat Tanda Tangan dan kelebihan beban §7.6.)
Anggota yang disintesis dijelaskan dalam subklaus berikut.
16.4.2 Anggota kesetaraan
Anggota kesetaraan yang disintesis mirip dengan anggota untuk kelas rekaman (§15.16.2), kecuali kurangnya EqualityContract, pemeriksaan null, atau pewarisan.
Struktur rekaman R mengimplementasikan System.IEquatable<R> dan mencakup kelebihan beban yang disintesis dengan jenis kuat dari Equals(R other), yang bersifat publik, sebagai berikut:
public readonly bool Equals(R other);
Metode ini dapat dinyatakan secara eksplisit. Namun, ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan.
Jika Equals(R other) ditentukan pengguna (yaitu, tidak disintesis) tetapi GetHashCode tidak, peringatan akan dihasilkan.
Yang disintesis akan mengembalikan true jika dan hanya jika untuk setiap bidang fieldN instans Equals(R) dalam rekaman menyusun nilai System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN), di mana TN adalah jenis bidang, adalah true.
Struktur rekaman mencakup disintesis == dan != operator yang setara dengan operator yang dinyatakan sebagai berikut:
public static bool operator==(R r1, R r2) => r1.Equals(r2);
public static bool operator!=(R r1, R r2) => !(r1 == r2);
Metode Equals yang == dipanggil oleh operator adalah metode yang Equals(R other) ditentukan di atas. Operator != mendelegasikan ke == operator. Ini adalah kesalahan jika operator dinyatakan secara eksplisit.
Struktur rekaman mencakup penimpaan yang disintesis yang setara dengan metode yang dideklarasikan sebagai berikut:
public override readonly bool Equals(object? obj);
Ini adalah kesalahan jika penimpaan dinyatakan secara eksplisit. Penimpaan yang disintesis akan kembali other is R temp && Equals(temp) di mana R adalah struktur catatan.
Struktur rekaman mencakup penimpaan yang disintesis yang setara dengan metode yang dideklarasikan sebagai berikut:
public override readonly int GetHashCode();
Metode ini dapat dinyatakan secara eksplisit.
Peringatan akan dilaporkan jika salah satu dari Equals(R) dan GetHashCode() secara eksplisit dinyatakan tetapi metode lainnya tidak.
Pengambilalihan yang disintesis GetHashCode() akan mengembalikan int hasil dari menggabungkan nilai System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) untuk setiap bidang fieldN instans dengan TN menjadi jenis fieldN.
Contoh: Pertimbangkan struktur catatan berikut:
record struct R1(T1 P1, T2 P2);Untuk ini, anggota kesetaraan yang disintesis akan menjadi sesuatu seperti:
struct R1 : IEquatable<R1> { public T1 P1 { get; set; } public T2 P2 { get; set; } public override bool Equals(object? obj) => obj is R1 temp && Equals(temp); public bool Equals(R1 other) { return EqualityComparer<T1>.Default.Equals(P1, other.P1) && EqualityComparer<T2>.Default.Equals(P2, other.P2); } public static bool operator==(R1 r1, R1 r2) => r1.Equals(r2); public static bool operator!=(R1 r1, R1 r2) => !(r1 == r2); public override int GetHashCode() { return HashCode.Combine( EqualityComparer<T1>.Default.GetHashCode(P1), EqualityComparer<T2>.Default.GetHashCode(P2));contoh akhir
16.4.3 Mencetak anggota
Struktur rekaman menyertakan metode yang disintesis yang setara dengan yang berikut ini:
private bool PrintMembers(System.Text.StringBuilder builder);
Metode ini melakukan tugas-tugas berikut:
- Untuk setiap anggota yang dapat dicetak struct rekaman (bidang publik non-statis dan anggota properti yang dapat dibaca), menambahkan nama anggota tersebut diikuti dengan "
=" diikuti oleh nilai anggota yang dipisahkan dengan ", “, - Mengembalikan true jika struct rekaman memiliki anggota yang dapat dicetak.
Untuk anggota yang memiliki jenis nilai, nilainya akan dikonversi ke representasi string.
Jika anggota rekaman yang dapat dicetak tidak menyertakan properti yang dapat dibaca dengan non-aksesorreadonlyget , maka yang disintesis PrintMembers adalah readonly. Tidak ada persyaratan untuk bidang readonly rekaman agar PrintMembers metode menjadi readonly.
Metode PrintMembers ini dapat dinyatakan secara eksplisit. Namun, ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan.
Struktur catatan mencakup metode yang disintesis yang setara dengan yang berikut ini:
public override string ToString();
Jika metode struct PrintMembers rekaman adalah readonly, maka metode yang disintesis ToString() adalah readonly.
Metode ini dapat dinyatakan secara eksplisit. Ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan.
Metode ini melakukan tugas-tugas berikut:
- Membuat instans
StringBuilder, - Menambahkan nama struct rekaman ke penyusun, diikuti dengan "
{", - Memanggil metode struct
PrintMembersrekaman yang memberinya penyusun, diikuti oleh "" jika mengembalikan true, - Menambahkan "
}", - Mengembalikan konten penyusun dengan
builder.ToString().
Contoh: Pertimbangkan struktur catatan berikut:
record struct R1(T1 P1, T2 P2);Untuk struktur catatan ini, anggota pencetakan yang disintesis akan menjadi sesuatu seperti:
struct R1 : IEquatable<R1> { public T1 P1 { get; set; } public T2 P2 { get; set; } private bool PrintMembers(StringBuilder builder) { builder.Append(nameof(P1)); builder.Append(" = "); builder.Append(this.P1); // or builder.Append(this.P1.ToString()); // if P1 has a value type builder.Append(", "); builder.Append(nameof(P2)); builder.Append(" = "); builder.Append(this.P2); // or builder.Append(this.P2.ToString()); // if P2 has a value type return true; } public override string ToString() { var builder = new StringBuilder(); builder.Append(nameof(R1)); builder.Append(" { "); if (PrintMembers(builder)) builder.Append(" "); builder.Append("}"); return builder.ToString(); } }contoh akhir
16.4.4 Anggota struct rekaman posisional
16.4.4.1 Umum
Selain menyediakan anggota yang dijelaskan dalam subklasus sebelumnya, struktur catatan posisi (§16.2.1) mensintesis anggota tambahan dengan kondisi yang sama dengan anggota lain, seperti yang dijelaskan dalam subklasus berikut.
16.4.4.2 Konstruktor utama
Struktur rekaman memiliki konstruktor publik yang tanda tangannya sesuai dengan parameter nilai deklarasi jenis. Ini disebut konstruktor utama untuk jenis tersebut. Ini adalah kesalahan untuk memiliki konstruktor utama dan konstruktor dengan tanda tangan yang sama yang sudah ada dalam struktur. Jika deklarasi jenis tidak menyertakan delimited_parameter_list, tidak ada konstruktor utama yang dihasilkan.
record struct R1 { public R1() { } // OK } record struct R2() { public R2() { } // error: 'R2' already defines // a constructor with the same parameter types }
Deklarasi bidang instans untuk struktur rekaman diizinkan untuk menyertakan penginisialisasi variabel. Jika tidak ada konstruktor utama, penginisialisasi instans dijalankan sebagai bagian dari konstruktor tanpa parameter. Jika tidak, pada runtime konstruktor utama menjalankan penginisialisasi instans yang muncul di record-struct-body.
Jika struktur rekaman memiliki konstruktor utama, setiap konstruktor yang ditentukan pengguna harus memiliki inisialisasi konstruktor eksplisit this yang memanggil konstruktor utama atau konstruktor yang dinyatakan secara eksplisit.
Parameter konstruktor utama serta anggota struktur rekaman berada dalam cakupan dalam penginisialisasi bidang atau properti instans. Anggota instans akan menjadi kesalahan di lokasi ini, tetapi parameter konstruktor utama akan berada dalam cakupan dan dapat digunakan dan akan membayangi anggota. Anggota statis juga akan dapat digunakan.
Peringatan akan dihasilkan jika parameter konstruktor utama tidak dibaca.
Aturan penugasan pasti untuk konstruktor instans struct berlaku untuk konstruktor utama struktur rekaman. Misalnya, berikut ini adalah kesalahan:
record struct Pos(int X) // def assignment error in primary constructor { private int x; public int X { get { return x; } set { x = value; } } = X; }
16.4.4.3 Properti
Untuk setiap parameter delimited_parameter_list yang memiliki nama dan jenis yang sama dengan bidang instans yang dideklarasikan secara eksplisit, sisa subklasul ini tidak berlaku.
Untuk setiap parameter struktur rekaman delimited_parameter_list ada anggota properti publik terkait yang nama dan jenisnya diambil dari deklarasi parameter nilai.
Untuk struktur rekaman:
properti publik
getdaninitotomatis dibuat jika struktur rekaman memilikireadonlypengubah,getdansetsebaliknya. Kedua jenis aksesor set (setdaninit) dianggap "cocok." Jadi, pengguna dapat mendeklarasikan properti khusus init sebagai pengganti properti yang dapat diubah yang disintesis.Properti yang diwariskan dengan jenis pencocokan
abstractditimpa.Tidak ada properti otomatis yang dibuat jika struktur rekaman memiliki bidang instans dengan nama dan jenis yang diharapkan.
Ini adalah kesalahan jika properti yang diwariskan tidak memiliki
publicgetdanset/initaksesor.Ini adalah kesalahan jika properti atau bidang yang diwariskan disembunyikan.
Properti otomatis diinisialisasi ke nilai parameter konstruktor utama yang sesuai.
Atribut dapat diterapkan ke properti otomatis yang disintesis dan bidang dukungannya dengan menggunakan
property:ataufield:target untuk atribut yang diterapkan secara sintetis ke parameter struct rekaman yang sesuai.
16.4.4.4 Deconstruct
Struktur rekaman posisional dengan setidaknya satu parameter mensintesis metode instans publik void-pengembalian yang disebut Deconstruct dengan deklarasi parameter keluar untuk setiap parameter deklarasi konstruktor utama. Setiap parameter Deconstruct memiliki jenis yang sama dengan parameter yang sesuai dari deklarasi konstruktor utama. Isi metode menetapkan setiap parameter metode Deconstruct ke nilai dari akses anggota instans ke anggota dengan nama yang sama.
Jika anggota instans yang diakses dalam isi tidak menyertakan properti dengan non-aksesorreadonlyget , maka metode yang disintesis Deconstruct adalah readonly.
Metode ini dapat dinyatakan secara eksplisit. Ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan, atau statis.
16.5 Perbedaan kelas dan struktur
16.5.1 Umum
Struktur berbeda dari kelas dengan beberapa cara penting:
- Struktur adalah jenis nilai (§16.5.2).
- Semua jenis struct secara implisit mewarisi dari kelas
System.ValueType(§16.5.3). - Penugasan ke variabel jenis struct membuat salinan nilai yang ditetapkan (§16.5.4).
- Nilai default struct adalah nilai yang dihasilkan dengan mengatur semua bidang ke nilai defaultnya (§16.5.5).
- Operasi tinju dan pembukaan kotak digunakan untuk mengonversi antara jenis struct dan jenis referensi tertentu (§16.5.6).
- Arti
thisberbeda dalam anggota struktur (§16.5.7). - 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.5.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.5.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 protectedatau 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.5.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.24.2.
16.5.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). Ketika struct tidak mendeklarasikan konstruktor instans tanpa parameter eksplisit, konstruktor default disintesis dan selalu mengembalikan nilai yang dihasilkan dari pengaturan semua bidang ke nilai defaultnya. Ekspresi default selalu menghasilkan nilai default yang diinisialisasi nol, bahkan ketika struct mendeklarasikan konstruktor instans tanpa parameter eksplisit (§16.4.9).
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.5.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.5.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.5.8 Penginisialisasi bidang
Seperti yang dijelaskan dalam §16.5.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. Bidang statis dan instans dari struktur diizinkan untuk menyertakan penginisialisasi variabel; namun, dalam kasus penginisialisasi bidang instans, setidaknya satu konstruktor instans juga harus dinyatakan, atau untuk struktur rekaman, delimited_parameter_list akan ada.
Contoh:
Console.WriteLine($"Point is {new Point()}"); struct Point { public int x = 1; public int y = 1; public Point() { } public override string ToString() { return "(" + x + ", " + y + ")"; } }Point is (1, 1)contoh akhir
Ketika konstruktor instans struct tidak memiliki inisialisasi konstruktor, konstruktor tersebut secara implisit melakukan inisialisasi yang ditentukan oleh variable_initializerdari bidang instans yang dideklarasikan dalam strukturnya. Ini sesuai dengan urutan penugasan yang dijalankan segera setelah masuk ke konstruktor.
Ketika konstruktor instans struct memiliki this() inisialisasi konstruktor yang mewakili konstruktor tanpa parameter default, konstruktor yang dinyatakan secara implisit menghapus semua bidang instans dan melakukan inisialisasi yang ditentukan oleh variable_initializerdari bidang instans yang dideklarasikan dalam strukturnya. Segera setelah masuk ke konstruktor, semua bidang jenis nilai diatur ke nilai defaultnya dan semua bidang jenis referensi diatur ke null. Segera setelah itu, urutan tugas yang sesuai dengan variable_initializerdijalankan.
field_declaration yang dinyatakan langsung di dalam struct_declaration memiliki struct_modifierreadonly akan memiliki field_modifierreadonly.
16.5.9 Konstruktor
Struct dapat mendeklarasikan konstruktor instans, dengan parameter nol atau lebih. Jika struct tidak memiliki konstruktor instans tanpa parameter yang dinyatakan secara eksplisit, satu disintesis, dengan aksesibilitas publik, 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). Dalam kasus seperti itu, penginisialisasi bidang instans apa pun diabaikan ketika konstruktor tersebut dijalankan.
Konstruktor instans tanpa parameter yang dinyatakan secara eksplisit harus memiliki aksesibilitas publik.
Contoh: Diberikan hal berikut:
using System; struct Point { int x = -1, y = -2; public Point(int x, int y) { this.x = x; this.y = y; } public override string ToString() { return "(" + x + ", " + y + ")"; } } class A { static void Main() { Console.WriteLine($"Point is {new Point()}"); Console.WriteLine($"Point is {new Point(0,0)}"); } }Point is (0, 0) Point is (0, 0)pernyataan keduanya membuat
Pointdenganxdanydiinisialisasi ke nol, yang dalam kasus panggilan ke konstruktor instans tanpa parameter, mungkin mengejutkan, karena kedua bidang instans memiliki penginisialisasi, tetapi tidak dijalankan.contoh akhir
Konstruktor instans struct tidak diizinkan untuk menyertakan inisialisasi konstruktor formulir base(argument_list), di mana argument_list bersifat opsional. Eksekusi konstruktor instans tidak akan mengakibatkan eksekusi konstruktor dalam jenis System.ValueTypedasar struct .
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.
Bidang instans (selain fixed bidang) pasti akan ditetapkan dalam konstruktor instans struktur yang tidak memiliki this() 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 pasti (§12.24.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 backing 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.5.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.5.5) dari jenis struct tidak memicu konstruktor statis. (Contohnya adalah nilai awal elemen dalam array.) catatan akhir
16.5.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.5.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.5.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.5.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.5.15 Batasan konteks aman
16.5.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.5.15.2 Konteks aman parameter
Parameter dari jenis struct ref, termasuk parameter this dari metode instans, memiliki konteks pemanggil yang aman.
16.5.15.3 Konteks aman 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.5.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.5.15.5 Operator
Aplikasi operator yang ditentukan pengguna diperlakukan sebagai pemanggilan metode (§16.5.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.5.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.5.15.7 stackalloc
Hasil ekspresi stackalloc memiliki konteks aman dari anggota fungsi.
16.5.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 pada bidangrefyang tidak menjadi konstruksi yang valid di C# atau .NET. catatan akhir
ECMA C# draft specification