Baru-baru ini, Visual Studio 2019 Preview 2 dirilis. Dan dengan itu, beberapa fitur C # 8.0 tambahan siap untuk Anda coba. Ini terutama merupakan perbandingan dengan sampel, meskipun pada akhirnya saya akan menyentuh beberapa berita dan perubahan lainnya.
Artikel ini dalam bahasa Inggris.

Terima kasih telah menerjemahkan MSP kami,
Lev Bulanov .
Lebih banyak pola di lebih banyak tempat
Ketika pencocokan pola muncul di C # 7.0, kami mencatat bahwa peningkatan jumlah pola di lebih banyak tempat diharapkan di masa depan. Waktunya telah tiba! Kami menambahkan apa yang kami sebut pola rekursif, serta bentuk ekspresi saklar yang lebih ringkas yang disebut (Anda tebak).
Untuk memulai, berikut adalah contoh sederhana pola C # 7.0:
class Point { public int X { get; } public int Y { get; } public Point(int x, int y) => (X, Y) = (x, y); public void Deconstruct(out int x, out int y) => (x, y) = (X, Y); } static string Display(object o) { switch (o) { case Point p when pX == 0 && pY == 0: return "origin"; case Point p: return $"({pX}, {pY})"; default: return "unknown"; } }
Alihkan ekspresi
Pertama, perhatikan bahwa banyak ekspresi switch , pada kenyataannya, tidak melakukan banyak pekerjaan yang menarik dalam kasus tubuh. Seringkali semuanya hanya membuat nilai, baik dengan menetapkannya ke variabel atau dengan mengembalikannya (seperti yang ditunjukkan di atas). Dalam semua situasi ini, peralihan tampaknya tidak pada tempatnya. Ini mirip dengan fitur bahasa lima puluh tahun.
Kami memutuskan sudah waktunya untuk menambahkan formulir pernyataan beralih . Ini berlaku untuk contoh di bawah ini:
static string Display(object o) { return o switch { Point p when pX == 0 && pY == 0 => "origin", Point p => $"({pX}, {pY})", _ => "unknown" }; }
Ada beberapa hal yang telah berubah dibandingkan dengan beralih pernyataan. Mari kita daftar mereka:
- Kata kunci sakelar adalah “infix” antara nilai yang diuji dan daftar case {...} . Ini membuatnya lebih komposisional dengan ekspresi lain, dan juga lebih mudah untuk secara visual membedakan dari pernyataan switch.
- Kata kunci dan simbol kasus : telah diganti dengan panah lambda => singkatnya.
- Default untuk singkatnya telah diganti oleh pola reset.
- Tubuh adalah ekspresi. Hasil dari body yang dipilih menjadi hasil dari pernyataan switch.
Karena ekspresi harus penting atau melempar pengecualian, ekspresi pilih yang berakhir tanpa kecocokan akan melempar pengecualian. Kompiler akan memperingatkan Anda ketika ini bisa terjadi, tetapi itu tidak akan memaksa Anda untuk mengakhiri semua pernyataan pilih dengan fungsi catch-all.
Karena metode Tampilan kami sekarang terdiri dari pernyataan pengembalian tunggal, kami dapat menyederhanakannya untuk ekspresi:
static string Display(object o) => o switch { Point p when pX == 0 && pY == 0 => "origin", Point p => $"({pX}, {pY})", _ => "unknown" };
Apa pun rekomendasi pemformatan yang diberikan, mereka harus sangat jelas dan ringkas. Singkatnya memungkinkan Anda untuk memformat sakelar dengan cara "tabular", seperti dijelaskan di atas, dengan pola dan bod pada baris yang sama, dan => berbaris di bawah satu sama lain.
Ngomong-ngomong, kami berencana untuk mengizinkan penggunaan koma setelah kasus terakhir sesuai dengan semua "daftar yang dipisahkan oleh koma dalam kurung keriting" di C #, tetapi ini belum diizinkan dalam Pratinjau 2.
Properti Pola
Berbicara tentang singkatnya, pola tiba-tiba menjadi elemen tersulit dari ekspresi pilihan. Mari kita lakukan sesuatu.
Perhatikan bahwa ekspresi pilih menggunakan pola tipe Point p (dua kali), serta kapan menambahkan kondisi tambahan dalam kasus pertama.
Dalam C # 8.0, kami menambahkan elemen opsional tambahan ke jenis pola, yang memungkinkan pola itu sendiri masuk lebih dalam ke nilai yang cocok dengan pola. Anda dapat menjadikannya pola properti dengan menambahkan {...} yang berisi pola bersarang, menerapkan nilai ke properti atau bidang yang tersedia. Ini memungkinkan kami untuk menulis ulang ekspresi switch sebagai berikut:
static string Display(object o) => o switch { Point { X: 0, Y: 0 } p => "origin", Point { X: var x, Y: var y } p => $"({x}, {y})", _ => "unknown" };
Kedua kasus masih memverifikasi bahwa o adalah Point . Dalam kasus pertama, pola konstanta 0 secara rekursif diterapkan pada properti X dan Y dari variabel p , memeriksa apakah mereka memiliki nilai ini. Dengan demikian, kita dapat menghilangkan kondisi saat ini dan kasus serupa lainnya.
Dalam kasus kedua, pola var diterapkan ke masing-masing X dan Y. Ingat bahwa pola var di C # 7.0 selalu berhasil dan cukup mendeklarasikan variabel baru untuk menyimpan nilai. Jadi x dan y berisi nilai int untuk pX dan pY .
Kami tidak pernah menggunakan p dan benar-benar dapat melewatkannya di sini:
Point { X: 0, Y: 0 } => "origin", Point { X: var x, Y: var y } => $"({x}, {y})", _ => "unknown"
Satu hal yang tetap sama untuk semua jenis pola, termasuk pola properti, ini adalah persyaratan bahwa nilainya tidak nol. Ini membuka kemungkinan untuk menggunakan pola "kosong" {} pola sebagai pola "non-nol" kompak. Sebagai contoh. kita bisa mengganti fallback dengan dua kasus berikut:
{} => o.ToString(), null => "null"
{} Berurusan dengan objek non-nol yang tersisa, dan nol mendapat nol, sehingga sakelar ini lengkap, dan kompiler tidak akan mengeluh tentang nilai yang hilang.
Pola posisi
Pola properti tidak memperpendek kasus Poin kedua . Tidak perlu khawatir tentang ini, Anda dapat melakukan lebih banyak lagi.
Perhatikan bahwa kelas Point memiliki metode Dekonstruksi , yang disebut dekonstruktor. Dalam C # 7.0, dekonstruktur memungkinkan Anda untuk "mendekonstruksi" nilai ketika ditugaskan, sehingga Anda dapat menulis, misalnya:
(int x, int y) = GetPoint();
C # 7.0 tidak mengintegrasikan dekonstruksi dengan pola. Ini berubah dengan pola posisi, yang merupakan cara tambahan untuk memperluas jenis pola di C # 8.0. Jika jenis yang cocok adalah tipe tuple atau memiliki dekonstruktor, kita dapat menggunakan pola posisi sebagai cara ringkas untuk menerapkan pola rekursif tanpa harus menyebutkan properti:
static string Display(object o) => o switch { Point(0, 0) => "origin", Point(var x, var y) => $"({x}, {y})", _ => "unknown" };
Setelah mencocokkan objek dengan Point , dekonstruktor diterapkan, dan pola bersarang diterapkan ke nilai yang dihasilkan.
Dekonstruksi tidak selalu tepat. Mereka harus ditambahkan hanya pada tipe-tipe yang benar-benar jelas nilainya. Misalnya, untuk kelas Point , Anda dapat mengasumsikan bahwa nilai pertama adalah X dan yang kedua adalah Y , sehingga ekspresi switch di atas jelas dan mudah dibaca.
Pola Tuple
Kasus khusus pola pola posisi yang sangat berguna adalah aplikasi mereka untuk tupel. Jika pernyataan sakelar diterapkan langsung ke ekspresi tupel, kami bahkan mengizinkan untuk menghilangkan set kurung tambahan, seperti pada sakelar (x, y, z) alih-alih sakelar ((x, y, z)) .
Pola Tuple bagus untuk menguji beberapa input sekaligus. Berikut ini adalah implementasi sederhana dari mesin negara:
static State ChangeState(State current, Transition transition, bool hasKey) => (current, transition) switch { (Opened, Close) => Closed, (Closed, Open) => Opened, (Closed, Lock) when hasKey => Locked, (Locked, Unlock) when hasKey => Closed, _ => throw new InvalidOperationException($"Invalid transition") };
Tentu saja, kita dapat memasukkan hasKey dalam tuple daripada menggunakan ketika klausa - ini adalah masalah selera:
static State ChangeState(State current, Transition transition, bool hasKey) => (current, transition, hasKey) switch { (Opened, Close, _) => Closed, (Closed, Open, _) => Opened, (Closed, Lock, true) => Locked, (Locked, Unlock, true) => Closed, _ => throw new InvalidOperationException($"Invalid transition") };
Secara umum, Anda melihat bahwa pola rekursif dan beralih ekspresi dapat mengarah pada logika program yang lebih jelas dan deklaratif.
Fitur lain dari C # 8.0 di Pratinjau 2
Terlepas dari kenyataan bahwa dalam VS 2019 Pratinjau 2 fungsi utama untuk bekerja dengan pola adalah yang paling penting, ada beberapa yang lebih kecil yang, saya harap, Anda juga akan menemukan yang berguna dan menarik. Saya tidak akan merinci, cukup berikan deskripsi singkat masing-masing.
Menggunakan Iklan
Dalam menggunakan C # , operator selalu meningkatkan tingkat sarang, yang bisa sangat mengganggu dan mudah dibaca. Dalam kasus sederhana, ketika Anda hanya perlu menghapus sumber daya di akhir cakupan, gunakan deklarasi yang digunakan. Menggunakan deklarasi hanyalah deklarasi variabel lokal dengan menggunakan kata kunci di depannya, dan isinya ditempatkan di akhir blok instruksi saat ini. Karena itu, alih-alih:
static void Main(string[] args) { using (var options = Parse(args)) { if (options["verbose"]) { WriteLine("Logging..."); } ... }
Anda bisa menulis
static void Main(string[] args) { using var options = Parse(args); if (options["verbose"]) { WriteLine("Logging..."); } }
Struct ref sekali pakai
Referensi struct diperkenalkan di C # 7.2, dan tampaknya tidak ada tempat untuk mengulanginya di sini. Tapi tetap saja, ada baiknya diperhatikan sesuatu: mereka memiliki beberapa keterbatasan, seperti ketidakmungkinan mengimplementasikan antarmuka. Struct Ref sekarang dapat digunakan tanpa mengimplementasikan antarmuka IDisposable , cukup menggunakan metode Buang di dalamnya.
Fungsi lokal statis
Jika Anda ingin memastikan bahwa fungsi lokal Anda tidak menimbulkan biaya runtime yang terkait dengan variabel "menangkap" (referensi) dari ruang lingkup, Anda dapat mendeklarasikannya sebagai statis. Maka kompiler akan mencegah tautan ke segala sesuatu yang dideklarasikan dalam fungsi terlampir - kecuali untuk fungsi lokal statis lainnya!
Perubahan dari Pratinjau 1
Fungsi utama dari Pratinjau 1 adalah jenis referensi yang dapat dibatalkan dan aliran asinkron. Kedua fitur telah sedikit berubah di Pratinjau 2, jadi jika Anda mulai menggunakannya, ada baiknya untuk mengetahui hal berikut.
Jenis Referensi yang Tidak Dapat Diabaikan
Kami telah menambahkan lebih banyak opsi untuk mengelola peringatan yang dapat dibatalkan baik di sumbernya (melalui arahan #nullable dan #pragma warning ) dan di tingkat proyek. Kami juga mengubah langganan ke file proyek ke <NullableContextOptions> aktifkan </ NullableContextOptions> .
Utas asinkron
Kami mengubah bentuk antarmuka IAsyncEnumerable <T> yang diharapkan oleh kompiler. Ini mengarah pada fakta bahwa kompiler tidak disinkronkan dengan antarmuka yang disediakan di .NET Core 3.0 Preview 1, yang dapat menyebabkan beberapa masalah. Namun, .NET Core 3.0 Preview 2 akan segera dirilis dan ini akan mengembalikan sinkronisasi.