16 Struktur

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 ref pengubah.
  • 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 struct tidak boleh dikotak ke System.ValueType atau System.Object.
  • Jenis ref struct tidak boleh dinyatakan untuk mengimplementasikan antarmuka apa pun.
  • Metode instans yang dideklarasikan pada object atau pada System.ValueType, tetapi tidak ditimpa pada jenis ref struct, tidak boleh dipanggil dengan penerima dari jenis ref struct tersebut.

Catatan: Sebuah ref struct tidak boleh mendeklarasikan metode instance async atau menggunakan pernyataan yield return atau yield break dalam metode instance, karena parameter implisit this tidak 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 this adalah ref readonly referensi.
  • Anggota tidak boleh menetapkan ulang nilai this atau 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 this harus 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 readonlyAddMessage ini dapat mengubah status daftar pesan. Anggota InitializeMessages dapat menghapus dan menginisialisasi ulang daftar pesan. Dalam kasus AddMessage, pengubah readonly valid. Dalam kasus InitializeMessages, menambahkan pengubah readonly tidak 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:

  1. 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 ", “,
  2. 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:

  1. Membuat instans StringBuilder ,
  2. Menambahkan nama struct rekaman ke penyusun, diikuti dengan "{",
  3. Memanggil metode struct PrintMembers rekaman yang memberinya penyusun, diikuti oleh " " jika mengembalikan true,
  4. Menambahkan "}",
  5. 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 get dan init otomatis dibuat jika struktur rekaman memiliki readonly pengubah, get dan set sebaliknya. Kedua jenis aksesor set (set dan init) dianggap "cocok." Jadi, pengguna dapat mendeklarasikan properti khusus init sebagai pengganti properti yang dapat diubah yang disintesis.

  • Properti yang diwariskan dengan jenis pencocokan abstract 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 publicget dan set/init aksesor.

  • 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: atau field: 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 this berbeda 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 readonly meskipun 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 Node berisi bidang instans dengan jenisnya sendiri. Contoh lain

struct A { B b; }
struct B { C c; }
struct C { A a; }

adalah kesalahan karena masing-masing jenis A, B, dan C bergantung 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. Penugasan a untuk b membuat salinan nilai, dan b dengan demikian tidak terpengaruh oleh penugasan ke a.x. Sebaliknya Point telah dinyatakan sebagai kelas, outputnya adalah 100 karena a dan b akan 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 Point dinyatakan di atas, contoh

Point[] a = new Point[100];

menginisialisasi setiap Point dalam array ke nilai yang dihasilkan dengan cara mengatur bidang x dan y menjadi 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 null jika dipanggil secara eksplisit. Dalam kasus di mana variabel KeyValuePair tunduk pada inisialisasi nilai default, bidang key dan value akan berada dalam kondisi null, 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
3

Meskipun gaya yang buruk untuk ToString memiliki efek samping, contoh menunjukkan bahwa tidak ada tinju yang terjadi untuk tiga pemanggilan x.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 Increment memodifikasi nilai dalam variabel x. Ini tidak setara dengan panggilan kedua ke Increment, yang memodifikasi nilai dalam salinan yang terkotak dari x. Dengan demikian, output program adalah:

0
1
1

contoh 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 Point dengan x dan y diinisialisasi 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 X dan Y) yang dapat dipanggil sampai semua bidang struktur yang dibangun telah pasti ditetapkan. Namun, perhatikan bahwa jika Point merupakan 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.P memanggil aksesor M set jenis T ketika proses di §12.6.6.1 akan membuat salinan sementara .s
  • Jika s.P memanggil aksesor get jenis T, proses di §12.6.6.1 diikuti, termasuk membuat salinan s sementara 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 aman e1 harus berupa konteks pemanggil.
  • Untuk penugasan e1 = e2, konteks aman dari e2 harus setidaknya seluas konteks aman dari e1.

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 ekspresi foreach perulangan.
  • 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 bidang ref. Aturan keamanan yang dijelaskan dalam dokumen ini bergantung pada bidang ref yang tidak menjadi konstruksi yang valid di C# atau .NET. catatan akhir