Bagikan melalui


Jenis struktur (referensi C#)

Jenis struktur adalah jenis nilai yang dapat merangkum data dan fungsionalitas terkait. Anda menggunakan kata kunci struct 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 ref struct dan readonly ref struct jenis, lihat artikel jenis struktur ref.

Jenis struktur memiliki semantik nilai. Artinya, variabel jenis struktur berisi instans jenis. Secara default, nilai variabel disalin pada penugasan, meneruskan argumen ke metode, dan mengembalikan hasil metode. Untuk variabel berjenis struktur, instans jenis akan disalin. 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 menunjukkan angka (baik bilangan bulat maupun riil), nilai Boolean, karakter Unicode, 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 .

Struktur readonly

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

Hal ini menjamin bahwa tidak ada anggota struktur readonly yang memodifikasi status struktur. Itu berarti bahwa anggota instans lain 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 khusus init:

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

Anda juga dapat menggunakan 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.

Dalam anggota instans readonly, Anda tidak dapat menetapkan bidang instans struktur. Namun, anggota readonly dapat memanggil anggota non-readonly. Dalam hal ini, pengkompilasi membuat salinan instans struktur dan memanggil non-anggotareadonly pada salinan tersebut. Akibatnya, instans struktur asli tidak termodifikasi.

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 init pengakses:

    public readonly double X { get; init; }
    

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

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

Mutasi tidak merusak

Dimulai dari C# 10, Anda dapat menggunakan ekspresi with untuk menghasilkan salinan instans berjenis struktur dengan properti dan bidang yang ditentukan sudah termodifikasi. Gunakan sintaks penginisialisasi objek untuk menentukan anggota yang akan diubah dan nilai barunya, seperti yang ditunjukkan oleh 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)
}

Struktur record

Dimulai dari C# 10, 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 sebaris

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

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

Array sebaris adalah struktur yang berisi blok elemen N yang berdampingan dengan jenis yang sama. Ini adalah kode aman yang setara dengan deklarasi buffer tetap yang hanya tersedia dalam kode yang 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, array sebaris dapat diakses seperti array, baik untuk membaca dan menulis nilai. Selain itu, Anda dapat menggunakan operator rentang dan indeks .

Ada batasan minimal pada jenis bidang 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 sebaris 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 speklet fitur

Inisialisasi dan nilai default struktur

Variabel jenis struct secara langsung menampung data untuk struct tersebut. Hal ini akan membuat perbedaan antara struct yang tidak terinisialisasi, yang memiliki nilai default-nya dan struct yang diinisialisasi, yang menyimpan nilai yang ditetapkan dengan membuatnya. Sebagai contoh, perhatikan 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 ditugaskan secara pasti saat dibuat karena struct jenis langsung menyimpan data mereka. Nilai default struktur telah dengan pasti menetapkan semua bidang ke 0. Semua bidang harus dengan pasti ditetapkan ketika konstruktor dipanggil. Anda menginisialisasi bidang menggunakan mekanisme berikut:

  • Anda dapat menambahkan penginisialisasi bidang ke bidang apa pun atau properti yang diimplementasikan secara otomatis.
  • Anda dapat menginisialisasi bidang atau properti otomatis pada isi konstruktor.

Dimulai dari C# 11, jika Anda tidak menginisialisasi semua bidang dalam struktur, pengompilasi akan menambahkan kode ke konstruktor yang menginisialisasi bidang tersebut ke nilai default. Pengompilasi melakukan analisis penugasan defintif yang biasa dilakukan. Setiap bidang yang diakses sebelum ditetapkan, atau tidak secara pasti ditetapkan ketika konstruktor selesai menjalankannya diberi nilai default-nya sebelum isi konstruktor dijalankan. Jika this diakses sebelum semua bidang ditetapkan, struktur akan diinisialisasi ke nilai default sebelum isi konstruktor dijalankan.

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, konstruktor tersebut harus publik. Jika struktur mendeklarasikan penginisialisasi bidang apa pun, struct harus secara eksplisit mendeklarasikan konstruktor. Konstruktor itu tidak perlu 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 tanpa penginisialisasi bidang atau penugasan 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 struct isi, dalam deklarasi anggota untuk struktur tersebut.

Jika semua bidang instans dari jenis struktur dapat diakses, Anda juga dapat membuat instans tanpa operator new. Dalam hal ini Anda harus menginisialisasi semua bidang instans sebelum penggunaan instans yang pertama kalinya. 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)
    }
}

Dalam kasus jenis nilai bawaan, gunakan harfiah yang sesuai untuk menentukan nilai jenis.

Batasan dengan desain jenis struktur

Struktur memiliki sebagian besar kemampuan jenis kelas. Terdapat beberapa pengecualian dan beberapa pengecualian yang telah dihapus dalam versi yang lebih baru:

  • 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.
  • Sebelum C# 11, konstruktor jenis struktur harus menginisialisasi semua bidang instans dari jenis tersebut.

Meneruskan variabel berjenis struktur dengan 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 dengan nilai dapat memengaruhi performa kode Anda dalam skenario performa tinggi yang melibatkan jenis 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 Menghindari alokasi.

batasan struktur

Anda juga menggunakan kata kunci struct dalam batasan struct 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), ada konversi tinju dan buka kotak ke dan dari System.ValueType jenis dan System.Object . Terdapat juga konversi boxing dan unboxing antara jenis struktur dan antarmuka apa pun yang diimplementasikannya.

Spesifikasi bahasa C#

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

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

Lihat juga