Bagikan melalui


Jenis struktur (referensi C#)

Jenis strukturadalah jenis nilai yang dapat merangkum data dan fungsionalitas terkait.

Referensi bahasa C# mendanai versi bahasa C# yang terbaru dirilis. Ini juga berisi dokumentasi awal untuk fitur dalam pratinjau publik untuk rilis bahasa yang akan datang.

Dokumentasi mengidentifikasi fitur apa pun yang pertama kali diperkenalkan dalam tiga versi terakhir bahasa atau dalam pratinjau publik saat ini.

Tip

Untuk menemukan kapan fitur pertama kali diperkenalkan di C#, lihat artikel tentang riwayat versi bahasa C#.

struct Gunakan kata kunci untuk menentukan jenis struktur:

public struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; }
    public double Y { get; }

    public override string ToString() => $"({X}, {Y})";
}

Untuk informasi tentang jenis ref struct dan readonly ref struct, lihat artikel jenis struktur ref.

Jenis struktur memiliki semantik nilai. Artinya, variabel dari jenis struktur berisi satu contoh dari jenis tersebut. Secara default, sistem menyalin nilai variabel pada penugasan, saat meneruskan argumen ke metode, dan saat mengembalikan hasil metode. Untuk variabel jenis struktur, sistem menyalin instans jenis. Untuk informasi selengkapnya, lihat Jenis nilai.

Biasanya, Anda menggunakan jenis struktur untuk mendesain jenis data-sentris kecil yang memberikan perilaku yang hanya sedikit atau tidak sama sekali. Misalnya, .NET menggunakan jenis struktur untuk mewakili angka (bilangan bulat dan nyata), nilai Boolean, karakter Unicode, dan instans waktu. Jika Anda berfokus pada perilaku jenis, pertimbangkan untuk menentukan kelas. Jenis kelas memiliki semantik referensi. Artinya, variabel jenis kelas berisi referensi untuk instans jenis, bukan instans itu sendiri.

Karena jenis struktur memiliki semantik nilai, kami sarankan Anda menentukan jenis struktur yang tidak dapat diubah .

readonly struktur

Gunakan pengubah readonly untuk menyatakan bahwa jenis struktur tidak dapat diubah. Semua anggota data dari struktur readonly harus bersifat baca-saja sebagai berikut:

  • Deklarasi bidang apa pun harus memiliki pengubah readonly.
  • Properti apa pun, termasuk yang diimplementasikan secara otomatis, harus bersifat hanya dapat dibaca atau init saja. Setter khusus init hanya tersedia dari C# versi 9 dan seterusnya.

Aturan ini menjamin bahwa tidak ada anggota struktur yang readonly memodifikasi status struktur. Semua anggota instans lainnya kecuali konstruktor secara readonlyimplisit .

Catatan

Dalam struktur readonly, anggota data dari jenis referensi yang dapat diubah masih dapat mengubah statusnya sendiri. Misalnya, Anda tidak dapat mengganti instans List<T>, tetapi Anda dapat menambahkan elemen baru ke dalamnya.

Kode berikut mendefinisikan readonly struct dengan setter properti yang hanya dapat diinisialisasi:

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString() => $"({X}, {Y})";
}

Anggota instans readonly

Gunakan pengubah readonly untuk menyatakan bahwa anggota instans tidak mengubah status struct. Jika Anda tidak dapat mendeklarasikan seluruh jenis struktur sebagai readonly, gunakan pengubah readonly untuk menandai anggota instans yang tidak mengubah status struktur.

readonly Dalam anggota instans, Anda tidak dapat menetapkan ke bidang instans struktur. Namun, anggota readonly dapat memanggil anggota non-readonly. Dalam hal ini, kompilator membuat salinan instance struktur dan memanggil anggota non-readonly pada salinan tersebut. Akibatnya, instans struktur asli tidak diubah.

Biasanya, Anda menerapkan pengubah readonly ke jenis anggota instans berikut:

  • Metode:

    public readonly double Sum()
    {
        return X + Y;
    }
    

    Anda juga dapat menerapkan pengubah readonly ke metode yang mengambil alih metode yang dideklarasikan dalam System.Object:

    public readonly override string ToString() => $"({X}, {Y})";
    
  • Properti dan pengindeks:

    private int counter;
    public int Counter
    {
        readonly get => counter;
        set => counter = value;
    }
    

    Jika Anda perlu menerapkan pengubah readonly baik ke pengakses properti maupun pengindeks, terapkan pengubah dalam deklarasi properti atau pengindeks.

    Catatan

    Kompilator mendeklarasikan get pengakses properti yang diimplementasikan secara otomatis sebagai readonly, terlepas dari readonly keberadaan pengubah dalam deklarasi properti.

    Anda dapat menerapkan pengubah readonly ke properti atau pengindeks dengan pengakses init:

    public readonly double X { get; init; }
    

Anda dapat menerapkan pengubah readonly ke bidang statis dari jenis struktur, tetapi tidak untuk anggota statis lainnya, seperti properti atau metode.

Pengkompilasi dapat menggunakan pengubah readonly untuk pengoptimalan performa. Untuk informasi selengkapnya, lihat Menghindari alokasi.

Mutasi tidak merusak

with Gunakan ekspresi untuk membuat salinan instans jenis struktur dengan properti dan bidang yang ditentukan diubah. Gunakan sintaks penginisialisasi objek untuk menentukan anggota mana yang akan diubah dan nilai barunya, seperti yang ditunjukkan contoh berikut:

public readonly struct Coords
{
    public Coords(double x, double y)
    {
        X = x;
        Y = y;
    }

    public double X { get; init; }
    public double Y { get; init; }

    public override string ToString() => $"({X}, {Y})";
}

public static void Main()
{
    var p1 = new Coords(0, 0);
    Console.WriteLine(p1);  // output: (0, 0)

    var p2 = p1 with { X = 3 };
    Console.WriteLine(p2);  // output: (3, 0)

    var p3 = p1 with { X = 1, Y = 4 };
    Console.WriteLine(p3);  // output: (1, 4)
}

record struktur

Anda dapat menentukan jenis struktur rekaman. Jenis rekaman menyediakan fungsionalitas bawaan untuk merangkum data. Anda dapat menentukan baik jenis record struct maupun readonly record struct. Struktur rekaman tidak boleh berupa ref struct. Untuk informasi dan contoh selengkapnya, lihat Rekaman.

Array dalam baris

Dimulai dengan C# 12, Anda dapat mendeklarasikan array sebaris sebagai struct jenis:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBuffer
{
    private char _firstElement;
}

Array inline adalah struktur yang berisi blok bersebelahan dari N elemen yang sejenis. Ini adalah setara kode aman untuk deklarasi buffer tetap yang hanya tersedia dalam kode tidak aman. Array sebaris adalah struct dengan karakteristik berikut:

  • Ini berisi satu bidang.
  • Struktur tidak menentukan tata letak eksplisit.

Selain itu, pengkompilasi memvalidasi System.Runtime.CompilerServices.InlineArrayAttribute atribut:

  • Panjangnya harus lebih besar dari nol (> 0).
  • Jenis target harus berupa struct.

Dalam kebanyakan kasus, Anda dapat mengakses array sebaris seperti array, baik untuk membaca dan menulis nilai. Anda juga dapat menggunakan operator rentang dan indeks .

Ada restriksi minimal pada jenis elemen tunggal dari array sebaris. Ini tidak bisa menjadi jenis penunjuk:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithPointer
{
    private unsafe char* _pointerElement;    // CS9184
}

Tetapi dapat berupa jenis referensi apa pun, atau jenis nilai apa pun:

[System.Runtime.CompilerServices.InlineArray(10)]
public struct CharBufferWithReferenceType
{
    private string _referenceElement;
}

Anda dapat menggunakan array sebaris dengan hampir semua struktur data C#.

Array inline adalah fitur bahasa tingkat lanjut. Elemen ini ditujukan untuk skenario performa tinggi di mana blok elemen yang sebaris dan berdampingan lebih cepat daripada struktur data alternatif lainnya. Anda dapat mempelajari selengkapnya tentang array sebaris dari spesifikasi fitur.

Inisialisasi struktur data dan nilai default

Variabel jenis struct secara langsung menampung data untuk struct tersebut. Penyimpanan data langsung ini menciptakan perbedaan antara yang tidak diinisialisasi struct, yang memiliki nilai default, dan yang diinisialisasi struct, yang menyimpan nilai yang ditetapkan dengan membangunnya. Misalnya, pertimbangkan kode berikut:

public readonly struct Measurement
{
    public Measurement()
    {
        Value = double.NaN;
        Description = "Undefined";
    }

    public Measurement(double value, string description)
    {
        Value = value;
        Description = description;
    }

    public double Value { get; init; }
    public string Description { get; init; }

    public override string ToString() => $"{Value} ({Description})";
}

public static void Main()
{
    var m1 = new Measurement();
    Console.WriteLine(m1);  // output: NaN (Undefined)

    var m2 = default(Measurement);
    Console.WriteLine(m2);  // output: 0 ()

    var ms = new Measurement[2];
    Console.WriteLine(string.Join(", ", ms));  // output: 0 (), 0 ()
}

Seperti yang ditunjukkan contoh sebelumnya, ekspresi nilai default mengabaikan konstruktor tanpa parameter dan menghasilkan nilai default jenis struktur. Instansiasi array berjenis struktur juga mengabaikan konstruktor tanpa parameter dan menghasilkan array yang berisikan nilai default jenis struktur.

Situasi paling umum di mana Anda melihat nilai default adalah dalam array atau di koleksi lain di mana penyimpanan internal menyertakan blok variabel. Contoh berikut membuat array 30 struktur TemperatureRange, yang masing-masing memiliki nilai default:

// All elements have default values of 0:
TemperatureRange[] lastMonth = new TemperatureRange[30];

Semua bidang anggota struct harus pasti ditetapkan saat dibuat karena jenis struct menyimpan data mereka secara langsung. Nilai default struct pasti menetapkan semua bidang ke 0. Semua variabel harus benar-benar ditetapkan saat konstruktor dipanggil. Anda menginisialisasi bidang dengan menggunakan mekanisme berikut:

  • Tambahkan penginisialisasi bidang ke bidang atau properti yang diimplementasikan secara otomatis.
  • Menginisialisasi bidang atau properti otomatis apa pun dalam isi konstruktor.

Jika Anda tidak menginisialisasi semua bidang dalam struct, pengkompilasi menambahkan kode ke konstruktor yang menginisialisasi bidang tersebut ke nilai default. Struktur yang ditetapkan ke nilai default diinisialisasi ke pola bit 0. Struktur yang diinisialisasi dengan new diinisialisasi ke pola 0-bit, diikuti dengan menjalankan inisialisasi bidang dan konstruktor.

public readonly struct Measurement
{
    public Measurement(double value)
    {
        Value = value;
    }

    public Measurement(double value, string description)
    {
        Value = value;
        Description = description;
    }

    public Measurement(string description)
    {
        Description = description;
    }

    public double Value { get; init; }
    public string Description { get; init; } = "Ordinary measurement";

    public override string ToString() => $"{Value} ({Description})";
}

public static void Main()
{
    var m1 = new Measurement(5);
    Console.WriteLine(m1);  // output: 5 (Ordinary measurement)

    var m2 = new Measurement();
    Console.WriteLine(m2);  // output: 0 ()

    var m3 = default(Measurement);
    Console.WriteLine(m3);  // output: 0 ()
}

Setiap struct memiliki konstruktor tanpa parameter public. Jika Anda menulis konstruktor tanpa parameter, maka konstruktor tersebut harus yang publik. Jika struktur mendeklarasikan penginisialisasi bidang apa pun, struct harus secara eksplisit mendeklarasikan konstruktor. Konstruktor tersebut tidak harus tanpa parameter. Jika struct mendeklarasikan penginisialisasi bidang tetapi tidak ada konstruktor, kompilator melaporkan kesalahan. Setiap konstruktor yang dideklarasikan secara eksplisit (dengan parameter, atau tanpa parameter) menjalankan semua penginisialisasi bidang untuk struktur tersebut. Semua bidang yang tidak memiliki penginisialisasi atau pengaturan dalam konstruktor diatur ke nilai default. Untuk informasi selengkapnya, lihat catatan proposal fitur konstruktor struktur tanpa parameter.

Dimulai dengan C# 12, struct jenis dapat menentukan konstruktor utama sebagai bagian dari deklarasinya. Konstruktor utama menyediakan sintaks ringkas untuk parameter konstruktor yang dapat digunakan di seluruh isi struct, dalam deklarasi anggota apa pun untuk struktur tersebut.

Jika semua bidang instans dari tipe struktur dapat diakses, Anda juga dapat menginstansiasi tanpa operator new. Dalam hal ini, Anda harus menginisialisasi semua bidang instans sebelum penggunaan instans pertama. Contoh berikut ini menunjukkan cara melakukannya:

public static class StructWithoutNew
{
    public struct Coords
    {
        public double x;
        public double y;
    }

    public static void Main()
    {
        Coords p;
        p.x = 3;
        p.y = 4;
        Console.WriteLine($"({p.x}, {p.y})");  // output: (3, 4)
    }
}

Untuk jenis nilai bawaan , gunakan literal yang sesuai untuk menentukan nilai jenisnya.

Batasan dengan desain jenis struktur

Struktur memiliki sebagian besar kapabilitas dari jenis kelas. Ada beberapa pengecualian:

  • Jenis struktur tidak dapat mewarisi dari kelas atau jenis struktur lain dan tidak dapat menjadi dasar kelas. Namun, jenis struktur dapat mengimplementasikan antarmuka.
  • Anda tidak dapat mendeklarasikan finalizer dalam jenis struktur.
  • Konstruktor jenis struktur harus menginisialisasi semua bidang instans jenis.

Meneruskan variabel bertipe struktur melalui referensi

Saat Anda meneruskan variabel berjenis struktur ke metode sebagai argumen atau mengembalikan nilai jenis struktur dari metode, seluruh instans jenis struktur akan disalin. Meneruskan nilai dapat memengaruhi kinerja kode Anda dalam skenario berkinerja tinggi yang melibatkan tipe struktur besar. Anda dapat menghindari penyalinan nilai dengan meneruskan variabel berjenis struktur dengan referensi. refGunakan pengubah parameter metode , out, in, atau ref readonly untuk menunjukkan bahwa argumen harus diteruskan oleh referensi. Gunakan pengembalian ref untuk mengembalikan hasil metode berdasarkan referensi. Untuk informasi selengkapnya, lihat Cara Menghindari Alokasi.

batasan struct

struct Gunakan kata kunci dalam struct batasan untuk menentukan bahwa parameter jenis adalah jenis nilai yang tidak dapat diubah ke null. Jenis struktur dan enumerasi memenuhi batasan struct.

Konversi

Untuk jenis struktur apa pun (kecuali ref struct jenis), konversi tinju dan pembukaan kotak ada ke dan dari System.ValueType jenis dan System.Object . Konversi tinju dan pembukaan kotak juga ada antara jenis struktur dan antarmuka apa pun yang diterapkannya.

Spesifikasi bahasa C#

Untuk informasi selengkapnya, lihat bagian Struktur dari Spesifikasi bahasa C#.

Untuk informasi selengkapnya tentang fitur struct, lihat catatan fitur berikut:

Lihat juga