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.
Nota
Artikel ini adalah spesifikasi fitur. Spesifikasi berfungsi sebagai dokumen desain untuk fitur tersebut. Ini termasuk perubahan spesifikasi yang diusulkan, bersama dengan informasi yang diperlukan selama desain dan pengembangan fitur. Artikel ini diterbitkan sampai perubahan spesifikasi yang diusulkan diselesaikan dan dimasukkan dalam spesifikasi ECMA saat ini.
Mungkin ada beberapa perbedaan antara spesifikasi fitur dan implementasi yang selesai. Perbedaan tersebut tercatat dalam catatan rapat desain bahasa yang relevan (LDM) .
Anda dapat mempelajari lebih lanjut tentang proses untuk mengadopsi speklet fitur ke dalam standar bahasa C# dalam artikel tentang spesifikasi .
Edisi Unggulan: https://github.com/dotnet/csharplang/issues/4334
Sintaks untuk struct rekaman adalah sebagai berikut:
record_struct_declaration
: attributes? struct_modifier* 'partial'? 'record' 'struct' identifier type_parameter_list?
parameter_list? struct_interfaces? type_parameter_constraints_clause* record_struct_body
;
record_struct_body
: struct_body
| ';'
;
Tipe struct rekaman adalah tipe nilai, seperti tipe struct lainnya. Mereka secara implisit mewarisi dari kelas System.ValueType.
Pengubah dan anggota struktur rekaman tunduk pada batasan yang sama dengan struktur (aksesibilitas pada jenis, pengubah pada anggota, base(...) inisialisasi konstruktor instans, penugasan pasti untuk this dalam konstruktor, destruktor, ...). Struktur rekaman juga akan mengikuti aturan yang sama dengan struktur untuk konstruktor instans tanpa parameter dan penginisialisasi bidang, tetapi dokumen ini mengasumsikan bahwa kami akan mencabut batasan tersebut untuk struktur umumnya.
Lihat §16.4.9 Lihat konstruktor struct tanpa parameter spesifikasi.
Struktur rekaman tidak dapat menggunakan pengubah ref.
Paling banyak satu deklarasi jenis parsial dari struktur rekaman parsial dapat memberikan parameter_list.
parameter_list mungkin kosong.
Parameter struct rekaman tidak dapat menggunakan pengubah ref, out, atau this (tetapi in dan params diizinkan).
Anggota struktur rekaman
Selain anggota yang dinyatakan dalam badan struktur rekaman, jenis struktur rekaman memiliki anggota sintesis tambahan. Anggota disintesis kecuali anggota dengan tanda tangan "cocok" dinyatakan dalam isi struktur rekaman atau anggota non-virtual konkret yang dapat diakses dengan tanda tangan "cocok" diwariskan. 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. Ini adalah kesalahan bagi anggota rekaman struct untuk diberi nama "Clone".
Ini adalah kesalahan jika bidang instans dari struct rekaman memiliki tipe yang tidak aman.
Struktur catatan tidak diizinkan untuk mendeklarasikan destruktor.
Anggota yang disintesis adalah sebagai berikut:
Anggota kelompok kesetaraan
Anggota kesetaraan yang disintesis mirip seperti di kelas rekaman (Equals untuk jenis ini, Equals untuk jenis object, dan operator == serta != untuk jenis ini),
kecuali tidak adanya EqualityContract, pemeriksaan nilai null, atau pewarisan.
Struktur rekaman mengimplementasikan System.IEquatable<R> dan menyertakan overload bertipe kuat yang disintesis dari Equals(R other) di mana R adalah struktur rekaman tersebut.
Metode ini public.
Metode ini dapat dinyatakan secara eksplisit. Ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan.
Jika Equals(R other) ditentukan pengguna (tidak disintesis) tetapi GetHashCode tidak, peringatan akan dihasilkan.
public readonly bool Equals(R other);
Equals(R) yang disintesis mengembalikan true jika dan hanya jika untuk setiap bidang instance fieldN dalam rekaman struct, nilai System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN) di mana TN adalah tipe bidang akan menjadi true.
Struktur rekaman mencakup operator == dan != yang disintesis 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 Equals(R other) yang 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 penggantian dinyatakan secara eksplisit.
Penimpaan yang disintesis mengembalikan other is R temp && Equals(temp) di mana R adalah struktur rekaman.
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 dilaporkan jika salah satu dari Equals(R) dan GetHashCode() dinyatakan secara eksplisit tetapi metode lainnya tidak eksplisit.
Penyusunan ulang GetHashCode() yang disintesis mengembalikan hasil int dari penggabungan nilai System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN) untuk setiap bidang instans fieldN, dengan TN sebagai tipe dari fieldN.
Misalnya, pertimbangkan struktur catatan berikut:
record struct R1(T1 P1, T2 P2);
Untuk struktur catatan ini, anggota kesetaraan yang disintesis akan mirip dengan:
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 Combine(
EqualityComparer<T1>.Default.GetHashCode(P1),
EqualityComparer<T2>.Default.GetHashCode(P2));
}
}
Mencetak anggota: Metode PrintMembers dan ToString
Struktur rekaman mencakup metode yang disintesis yang setara dengan metode yang dideklarasikan sebagai berikut:
private bool PrintMembers(System.Text.StringBuilder builder);
Metode ini melakukan hal berikut:
- untuk setiap anggota yang dapat dicetak dari struktur rekaman (field publik non-statis dan anggota properti yang dapat diakses), menambahkan nama anggota tersebut diikuti dengan " = " dan kemudian diikuti dengan nilai anggota yang dipisahkan dengan ", ",
- kembalikan true jika struct rekaman memiliki anggota yang bisa dicetak.
Untuk anggota yang memiliki jenis nilai, kami akan mengonversi nilainya menjadi representasi string menggunakan metode paling efisien yang tersedia untuk platform target. Saat ini berarti memanggil ToString sebelum meneruskan ke StringBuilder.Append.
Jika anggota rekaman yang dapat dicetak tidak menyertakan properti yang dapat dibaca dengan aksesor non-readonlyget, maka PrintMembers yang disintesis readonly. Tidak ada persyaratan untuk kolom rekaman seharusnya readonly agar metode PrintMembersreadonly.
Metode PrintMembers dapat dinyatakan secara eksplisit.
Ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan.
Struktur rekaman mencakup metode yang disintesis yang setara dengan metode yang dideklarasikan sebagai berikut:
public override string ToString();
Jika metode PrintMembers dari struktur rekaman adalah readonly, maka metode ToString() yang disintesis adalah readonly.
Metode ini dapat dinyatakan secara eksplisit. Ini adalah kesalahan jika deklarasi eksplisit tidak cocok dengan tanda tangan atau aksesibilitas yang diharapkan.
Metode yang disintesis:
- membuat sebuah instans
StringBuilder, - menambahkan nama struct rekaman ke penyusun, diikuti dengan " { ",
- memanggil metode
PrintMembersdari struktur rekaman dengan memberikan pembangun, diikuti dengan " " jika mengembalikan true, - menambahkan "}",
- mengembalikan konten penyusun dengan
builder.ToString().
Misalnya, pertimbangkan struktur catatan berikut:
record struct R1(T1 P1, T2 P2);
Untuk struktur catatan ini, anggota cetak yang disintesis akan terlihat 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();
}
}
Anggota struktur rekaman posisi
Selain anggota di atas, struktur rekam dengan daftar parameter ("rekaman berbasis posisi") menghasilkan anggota tambahan dengan kondisi yang sama dengan anggota di atas.
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 jika terdapat konstruktor utama dan konstruktor lain dengan tanda tangan yang sama dalam struct. Jika deklarasi jenis tidak menyertakan daftar parameter, tidak ada konstruktor utama yang dihasilkan.
record struct R1
{
public R1() { } // ok
}
record struct R2()
{
public R2() { } // error: 'R2' already defines constructor with 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 struct rekaman memiliki konstruktor utama, konstruktor yang ditentukan pengguna harus memiliki inisialisasi konstruktor this yang eksplisit yang memanggil konstruktor utama atau konstruktor yang dinyatakan secara eksplisit.
Parameter konstruktor utama serta anggota record struct berlaku dalam penginisialisasi bidang atau properti instans. Penggunaan anggota instans akan menyebabkan kesalahan di lokasi ini (mirip dengan bagaimana anggota instans berada dalam cakupan dalam penginisialisasi konstruktor reguler saat ini, tetapi akan menjadi kesalahan jika digunakan), tetapi parameter konstruktor utama akan berada dalam cakupan, dapat digunakan, dan akan membayangi anggota lainnya. Anggota statis juga akan dapat digunakan.
Peringatan 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) // definite assignment error in primary constructor
{
private int x;
public int X { get { return x; } set { x = value; } } = X;
}
Properti
Untuk setiap parameter record struct dari sebuah deklarasi record struct, ada anggota properti publik yang sesuai di mana nama dan jenisnya diambil dari deklarasi parameter nilai.
Untuk struktur rekaman:
- Properti otomatis
getdaninityang bersifat publik akan dibuat jika struct rekaman memiliki pengubahreadonly; jika tidak,getdanset. Kedua jenis aksesor set (setdaninit) dianggap "sesuai". Jadi pengguna dapat mendeklarasikan properti khusus init sebagai pengganti properti yang dapat diubah yang disintesis. ** Propertiabstractyang diwariskan dengan tipe yang sesuai ditimpa. 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 aksesorpublicgetdanset/init. 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 targetproperty:ataufield:untuk atribut yang diterapkan secara sintetis ke parameter struct rekaman yang sesuai.
Mendekonstruksi
Struktur record posisional dengan minimal satu parameter akan menyusun metode instans publik yang mengembalikan void bernama Deconstruct, dengan deklarasi parameter keluaran untuk setiap parameter deklarasi konstruktor utama. Setiap parameter metode 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 di dalam tubuh tidak menyertakan properti dengan aksesor non-readonlyget, maka metode Deconstruct yang disintesis 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.
Perbolehkan ekspresi with pada struktur
Sekarang sah bagi penerima dalam ekspresi with untuk memiliki tipe struct.
Di sisi kanan ekspresi with adalah member_initializer_list dengan urutan pemberian nilai ke pengidentifikasi , yang harus menjadi field instans atau properti yang dapat diakses dari tipe penerima.
Untuk penerima dengan jenis struct, penerima pertama kali disalin, kemudian setiap member_initializer diproses dengan cara yang sama seperti penugasan ke bidang atau akses properti dari hasil konversi.
Penugasan diproses dalam urutan leksikal.
Penyempurnaan pada rekaman
Izinkan record class
Sintaks yang ada untuk jenis rekaman memungkinkan record class dengan arti yang sama seperti record:
record_declaration
: attributes? class_modifier* 'partial'? 'record' 'class'? identifier type_parameter_list?
parameter_list? record_base? type_parameter_constraints_clause* record_body
;
Izinkan anggota posisi yang ditentukan pengguna menjadi field
Tidak ada properti otomatis yang dibuat jika objek record memiliki atau mewarisi bidang instans dengan nama dan tipe yang diharapkan.
Izinkan konstruktor dan penginisialisasi anggota tanpa parameter dalam struktur
Lihat spesifikasi konstruktor struct tanpa parameter .
Buka pertanyaan
- bagaimana cara mengenali struktur rekaman dalam metadata? (kami tidak memiliki metode kloning yang tak terucapkan untuk dimanfaatkan ...)
Menjawab
- konfirmasikan bahwa kami ingin mempertahankan desain PrintMembers (metode terpisah yang mengembalikan
bool) (jawaban: ya) - konfirmasikan bahwa kami tidak akan mengizinkan
record ref struct(masalah dengan bidangIEquatable<RefStruct>dan ref) (jawaban: ya) - mengonfirmasi implementasi anggota kesetaraan. Alternatifnya adalah bahwa
bool Equals(R other),bool Equals(object? other)dan operator yang disintesis semuanya hanya mendelegasikan keValueType.Equals. (jawaban: ya) - konfirmasikan bahwa kami ingin mengizinkan penginisialisasi bidang ketika ada konstruktor utama. Apakah kita ingin juga mengizinkan konstruktor struct tanpa parameter sekalian (masalah Activator tampaknya sudah diperbaiki)? (jawaban: ya, spesifikasi yang sudah diperbarui harus ditinjau di LDM)
- berapa banyak yang ingin kita katakan tentang metode
Combine? (jawaban: sesedikitan mungkin) - haruskah kita melarang konstruktor yang didefinisikan pengguna dengan sifat konstruktor salinan? (jawaban: tidak, tidak ada konsep konstruktor penyalinan dalam spesifikasi struct rekaman)
- mengonfirmasi bahwa kami ingin melarang anggota bernama "Clone". (jawaban: benar)
- periksa kembali apakah logika
Equalsyang disintesis secara fungsional setara dengan implementasi runtime (misalnya float. NaN) (jawaban: dikonfirmasi di LDM) - mungkinkah atribut penargetan bidang atau properti ditempatkan dalam daftar parameter posisi? (jawaban: ya, sama seperti untuk kelas rekaman)
-
withdalam konteks umum? (jawaban: di luar cakupan untuk C# 10) - Apakah
GetHashCodeharus menyertakan hash dari jenisnya sendiri, agar mendapatkan nilai yang berbeda antararecord struct S1;danrecord struct S2;? (jawaban: tidak)
C# feature specifications