Apa yang baru di C# 15

C# 15 menyertakan fitur baru berikut. Coba fitur-fitur ini dengan menggunakan versi insider Visual Studio 2026 terbaru atau SDK pratinjau .NET 11:

C# 15 adalah rilis pratinjau C# terbaru. Versi pratinjau .NET 11 mendukung C# 15. Untuk informasi selengkapnya, lihat Penerapan versi bahasa C#.

Anda dapat mengunduh SDK pratinjau .NET 11 terbaru dari halaman unduhan .NET. Anda juga dapat mengunduh Visual Studio 2026 Insiders, yang mencakup SDK preview .NET 11.

Halaman "Apa yang baru di C#" menambahkan fitur baru saat tersedia dalam rilis pratinjau publik. Bagian kumpulan kerja pada halaman status fitur roslyn melacak ketika fitur-fitur baru digabungkan ke cabang utama.

Anda dapat menemukan perubahan yang merusak kompatibilitas yang diperkenalkan di C# 15 di artikel kami tentang perubahan yang merusak kompatibilitas.

Nota

Kami tertarik dengan umpan balik Anda tentang fitur-fitur ini. Jika Anda menemukan masalah dengan salah satu fitur baru ini, buat masalah baru di repositori dotnet/roslyn .

Argumen untuk ekspresi koleksi

Anda dapat meneruskan argumen ke konstruktor atau metode pabrik dari koleksi dasar dengan menggunakan elemen with(...) sebagai komponen awal dalam ekspresi koleksi. Fitur ini memungkinkan Anda menentukan kapasitas, pembanding, atau parameter konstruktor lainnya langsung dalam sintaks ekspresi koleksi.

Contoh berikut menunjukkan cara meneruskan argumen kapasitas ke List<T> konstruktor dan pembanding ke HashSet<T>:

string[] values = ["one", "two", "three"];

// Pass capacity argument to List<T> constructor
List<string> names = [with(capacity: values.Length * 2), .. values];

// Pass comparer argument to HashSet<T> constructor
HashSet<string> set = [with(StringComparer.OrdinalIgnoreCase), "Hello", "HELLO", "hello"];
// set contains only one element because all strings are equal with OrdinalIgnoreCase

Untuk mempelajari selengkapnya tentang argumen ekspresi koleksi, lihat artikel referensi bahasa tentang ekspresi koleksi atau spesifikasi fitur. Untuk informasi tentang menggunakan argumen ekspresi koleksi di penginisialisasi koleksi, lihat Penginisialisasi Objek dan Koleksi.

Jenis serikat pekerja

C# 15 memperkenalkan union types, yang mewakili nilai yang dapat menjadi salah satu dari beberapa tipe kasus. Nyatakan penyatuan union dengan kata kunci:

public record class Cat(string Name);
public record class Dog(string Name);
public record class Bird(string Name);

public union Pet(Cat, Dog, Bird);

Serikat pekerja menyediakan konversi implisit dari setiap jenis kasus, dan pengkompilasi memastikan switch ekspresi lengkap di semua jenis kasus:

Pet pet = new Dog("Rex");

string name = pet switch
{
    Dog d => d.Name,
    Cat c => c.Name,
    Bird b => b.Name,
};

Runtime ini mencakup tipe UnionAttribute dan IUnion mulai dari .NET 11 Preview 5. Beberapa fitur dari spesifikasi proposal belum diimplementasikan. Fitur-fitur tersebut akan hadir dalam pratinjau di masa mendatang.

Untuk informasi selengkapnya, lihat Jenis union dalam referensi bahasa atau spesifikasi fitur.

Hierarki tertutup

Mulai dari C# 15, Anda dapat menerapkan closed pengubah ke kelas untuk mendeklarasikan hierarki tertutup. Kelas closed hanya dapat diturunkan dari dalam assembly tempat kelas tersebut dideklarasikan, yang menetapkan kumpulan turunan langsung pada waktu kompilasi:

public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;

Karena kompiler mengetahui setiap subkelas langsung, ekspresi switch yang menangani masing-masing bersifat ekshaustif dan tidak memerlukan cabang default:

string Describe(GateState state) => state switch
{
    Closed => "closed",
    Open(var percent) => $"{percent}% open",
    // No warning: every direct descendant of 'GateState' is handled.
};

Pengubah closed adalah kata kunci kontekstual. Kelas closed secara abstract implisit dan tidak dapat dikombinasikan dengan sealed, , staticatau pengubah eksplisit abstract . Turunan tidak transitif: turunan tidak tertutup dari kelas tertutup masih dapat berasal dari rakitan lain. Untuk memperluas pemeriksaan kelengkapan ke seluruh hierarki, tandai juga turunan perantara closed.

Untuk informasi selengkapnya, lihat pola pengubah tertutup dan Hierarki tertutup dalam referensi bahasa, atau spesifikasi fitur.

Pengindeks ekstensi

Dimulai dengan C# 15, Anda dapat mendeklarasikan pengindeks dalam blok.extension Pengindeks ekstensi memungkinkan Anda mengakses objek penerima menggunakan indeks seolah-olah pengindeks tersebut dideklarasikan pada tipe objek penerima. Karena pengindeks selalu menjadi anggota instans, blok ekstensi yang menyatakan pengindeks harus menyediakan parameter penerima bernama.

Contoh berikut mendeklarasikan pengindeks get-only pada IEnumerable<int> yang mengembalikan elemen pada posisi tertentu:

public static class SequenceIndexer
{
    extension(IEnumerable<int> sequence)
    {
        public int this[int index] => sequence.ElementAt(index);
    }
}

Anda mengindeks penerima seolah-olah indeksator merupakan anggota dari tipe penerima:

IEnumerable<int> numbers = Enumerable.Range(1, 10);
int third = numbers[2];

Untuk informasi selengkapnya, lihat Deklarasi ekstensi dalam referensi bahasa atau spesifikasi fitur.

Berlabel break dan continue

Dimulai dengan C# 15, break dan continue pernyataan dapat memberi nama label pada konstruksi penutup. Gunakan pernyataan berlabel break untuk keluar dari loop yang mencakupnya atau dari pernyataan switch. Gunakan berlabel continue untuk memulai perulangan berikutnya dari perulangan yang tertutup.

break dan continue berlabel menggantikan solusi sementara yang biasanya akan Anda gunakan untuk mengarahkan alur kontrol melalui perulangan bersarang, seperti flag Boolean yang Anda tetapkan di dalam perulangan terdalam lalu periksa pada setiap tingkat luar, atau goto yang melompati perulangan-perulangan tersebut. Menyebutkan loop target secara langsung di pernyataan lompatan menghilangkan kerumitan itu dan membuat alur kontrol yang diinginkan lebih mudah dipahami.

outer: for (int row = 0; row < grid.Height; row++)
{
    for (int column = 0; column < grid.Width; column++)
    {
        if (grid[row, column].IsBlocked)
        {
            continue outer;
        }

        if (grid[row, column].IsGoal)
        {
            break outer;
        }
    }
}

Tempatkan label langsung pada perulangan atau pernyataan switch yang diidentifikasinya. Tanpa label, break dan continue pertahankan perilaku aslinya dan targetkan pernyataan yang paling dalam yang berlaku.

Aturan gaya IDE0410 menandai penanda Boolean dan pola goto yang dapat digantikan oleh pernyataan lompatan berlabel, serta menampilkan contoh masing-masing sebelum dan sesudahnya.

Untuk informasi selengkapnya, lihat Pernyataan lompat dalam referensi bahasa atau spesifikasi fitur.

Keamanan memori

C# 15 memulai upaya multirelease untuk menentukan ulang keamanan memori dalam bahasa. Tujuannya adalah untuk mengaitkan konteks unsafe dengan operasi yang benar-benar mengakses memori tak terkelola, alih-alih dengan keberadaan tipe pointer. Sebagian besar kerentanan keamanan memori berasal dari operasi akses ini, sehingga bahasa membuatnya menonjol bagi peninjau dan auditor.

Dalam model lengkap, unsafe pada anggota menandainya sebagai requires-unsafe: kewajiban audit beralih ke pemanggil, yang harus menggunakan anggota tersebut dalam konteks unsafe. Assembly memilih untuk menerapkan ini, dan pengkompilasi mencatat pilihan tersebut dengan atribut System.Runtime.CompilerServices.MemorySafetyRulesAttribute. Model tersebut juga menambahkan kata kunci kontekstual safe yang menandai member extern dan field dengan tata letak eksplisit sebagai aman. Bersama-sama, aturan ini membuat batas-batas potensi memori tidak aman secara eksplisit di seluruh program.

Langkah pertama mencakup relaksasi pointer. Saat Anda mengkompilasi dengan preview versi bahasa, operasi berikut tidak lagi memerlukan unsafe konteks:

  • Mendeklarasikan jenis penunjuk dan mengambil alamat variabel dengan & operator.
  • Pernyataan fixed , yang menyematkan variabel.
  • Mengonversi stackalloc ekspresi menjadi penunjuk.
  • Operator sizeof diterapkan pada semua jenis yang tidak dikelola.

Contoh berikut membuat dan menyematkan penunjuk tanpa unsafe konteks:

int number = 42;
int* pointer = &number;

int[] numbers = [10, 20, 30];
fixed (int* first = numbers)
{
    // Dereferencing the pointer still requires an unsafe context.
}

Operasi yang mengakses memori yang dirujuk oleh pointer, seperti dereferensi pointer (*p), akses anggota melalui pointer (p->member), akses elemen melalui pointer (p[i]), dan pemanggilan pointer fungsi, masih memerlukan konteks unsafe.

C# 15 juga menambahkan unsafe ekspresi, unsafe(expression), yang menetapkan konteks yang tidak aman untuk satu ekspresi. Ini berguna ketika blok unsafe tidak dapat muncul secara sintaksis, seperti inisialisasi bidang, inisialisasi konstruktor, atau filter catch:

class Header
{
    // A field initializer can't contain an unsafe block, but it can contain an unsafe expression.
    static readonly int Signature = unsafe(ReadSignature());

    static unsafe int ReadSignature()
    {
        int rawValue = 0x1234;
        int* pointer = &rawValue;
        return *pointer;
    }
}

Seperti bagian lain dari pratinjau keamanan memori, ekspresi unsafe memerlukan versi bahasa preview dan opsi kompilator AllowUnsafeBlocks.

Kompilator juga mengenali kata kunci kontekstual safe sebagai modifikator pada anggota extern dan bidang dengan tata letak eksplisit. Namun, model anggota requires-unsafe dan opt-in assembly ke aturan keamanan memori yang diperbarui belum tersedia, jadi safe dan unsafe saat ini tidak berdampak pada pemanggil.

Untuk informasi selengkapnya, lihat Kode tidak aman, jenis penunjuk, dan penunjuk fungsi dalam referensi bahasa atau spesifikasi fitur.

Baca juga