
Visual Studio 2019 Pratinjau 2 sudah keluar! Dan dengan itu, beberapa fitur C # 8.0 siap untuk Anda coba. Sebagian besar tentang pencocokan pola, meskipun saya akan menyentuh beberapa berita lain dan perubahan pada akhirnya.
Asli di BlogLebih banyak pola di lebih banyak tempat
Ketika C # 7.0 memperkenalkan pencocokan pola, kami mengatakan bahwa kami berharap untuk menambahkan lebih banyak pola di lebih banyak tempat di masa depan. Waktunya telah tiba! Kami menambahkan apa yang kami sebut pola rekursif , serta bentuk ekspresi yang lebih kompak dari pernyataan switch
yang disebut (Anda tebak!) Beralih ekspresi .
Berikut adalah contoh pola C # 7.0 sederhana untuk memulai kami:
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, mari kita amati bahwa banyak pernyataan switch
benar-benar tidak melakukan banyak pekerjaan menarik dalam badan-badan kasing. Seringkali mereka semua hanya menghasilkan nilai, baik dengan menugaskannya ke variabel atau dengan mengembalikannya (seperti di atas). Dalam semua situasi itu, pernyataan pergantian terus terang agak kikuk. Rasanya seperti fitur bahasa 5-dekade-tua itu, dengan banyak upacara.
Kami memutuskan sudah waktunya untuk menambahkan bentuk ekspresi switch
. Ini dia, diterapkan pada contoh di atas:
static string Display(object o) { return o switch { Point p when pX == 0 && pY == 0 => "origin", Point p => $"({pX}, {pY})", _ => "unknown" }; }
Ada beberapa hal di sini yang berubah dari pernyataan switch. Mari kita daftar mereka:
- Kata kunci
switch
adalah "infix" antara nilai yang diuji dan daftar case {...}
. Itu membuatnya lebih komposisional dengan ekspresi lain, dan juga lebih mudah untuk membedakan secara visual dari pernyataan switch. - Kata kunci
case
dan :
telah diganti dengan panah lambda =>
untuk singkatnya. default
telah diganti dengan pola _
discard for brevity.- Tubuh adalah ekspresi! Hasil dari body yang dipilih menjadi hasil dari ekspresi switch.
Karena ekspresi perlu memiliki nilai atau melempar pengecualian, ekspresi switch yang mencapai akhir tanpa kecocokan akan melempar pengecualian. Compiler melakukan pekerjaan yang baik untuk memperingatkan Anda ketika hal ini mungkin terjadi, tetapi tidak akan memaksa Anda untuk mengakhiri semua ekspresi switch dengan catch-all: Anda mungkin tahu lebih baik!
Tentu saja, karena metode Display
kami sekarang terdiri dari pernyataan pengembalian tunggal, kami dapat menyederhanakannya menjadi ekspresi-tubuh:
static string Display(object o) => o switch { Point p when pX == 0 && pY == 0 => "origin", Point p => $"({pX}, {pY})", _ => "unknown" };
Sejujurnya, saya tidak yakin panduan format apa yang akan kami berikan di sini, tetapi harus jelas bahwa ini jauh lebih jelas dan jelas, terutama karena singkatnya biasanya Anda dapat memformat sakelar dengan cara "tabular", seperti di atas , dengan pola dan badan pada baris yang sama, dan =>
berbaris di bawah satu sama lain.
Ngomong-ngomong, kami berencana untuk mengizinkan koma tertinggal ,
setelah kasus terakhir sesuai dengan semua "daftar yang dipisahkan koma dalam kurung kurawal" di C #, tetapi Pratinjau 2 belum mengizinkannya.
Pola properti
Berbicara tentang singkatnya, polanya tiba-tiba menjadi elemen terberat dari ekspresi saklar di atas! Mari kita lakukan sesuatu tentang itu.
Perhatikan bahwa ekspresi sakelar menggunakan pola tipe Point p
(dua kali), serta klausa when
untuk menambahkan kondisi tambahan untuk case
pertama.
Dalam C # 8.0 kami menambahkan lebih banyak elemen opsional ke pola tipe, yang memungkinkan pola itu sendiri menggali lebih jauh ke dalam nilai yang sedang dicocokkan dengan pola. Anda dapat menjadikannya pola properti dengan menambahkan {...}
mengandung pola bersarang untuk diterapkan pada properti atau bidang yang dapat diakses nilai. Ini mari kita 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 memeriksa bahwa o
adalah sebuah Point
. Kasus pertama kemudian menerapkan pola konstan 0
secara rekursif ke properti X
dan Y
dari p
, memeriksa apakah mereka memiliki nilai itu. Dengan demikian kita dapat menghilangkan klausa when
dalam hal ini dan banyak kasus umum.
Kasus kedua menerapkan pola var
untuk 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
bisa mengandung nilai int pX
dan pY
.
Kami tidak pernah menggunakan p
, dan bahkan dapat menghilangkannya di sini:
Point { X: 0, Y: 0 } => "origin", Point { X: var x, Y: var y } => $"({x}, {y})", _ => "unknown"
Satu hal yang tetap benar dari semua pola tipe termasuk pola properti, adalah bahwa mereka memerlukan nilai menjadi nol. Itu membuka kemungkinan pola properti "kosong" {}
digunakan sebagai pola "tidak-nol" yang ringkas. Misalnya, kita dapat mengganti kasing mundur dengan dua kasing berikut:
{} => o.ToString(), null => "null"
{}
Berurusan dengan objek nonnull yang tersisa, dan null
mendapatkan nulls, sehingga sakelarnya lengkap dan kompilator tidak akan mengeluh tentang nilai yang jatuh.
Pola posisi
Pola properti tidak benar-benar membuat Point
kedua lebih pendek , dan tampaknya tidak sepadan dengan masalah di sana, tetapi ada lebih banyak yang bisa dilakukan.
Perhatikan bahwa kelas Point
memiliki metode Deconstruct
, yang disebut dekonstruktor . Dalam C # 7.0, dekonstruktur memungkinkan nilai didekonstruksi pada tugas, sehingga Anda bisa menulis misalnya:
(int x, int y) = GetPoint();
C # 7.0 tidak mengintegrasikan dekonstruksi dengan pola. Itu berubah dengan pola posisi yang merupakan cara tambahan bahwa kita memperluas pola tipe di C # 8.0. Jika jenis yang cocok adalah tipe tuple atau memiliki dekonstruktor, kita dapat menggunakan pola posisi sebagai cara ringkas 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 objek dicocokkan sebagai sebuah Point
, dekonstruktor diterapkan, dan pola bersarang diterapkan ke nilai yang dihasilkan.
Dekonstruksi tidak selalu tepat. Mereka hanya boleh ditambahkan ke tipe yang benar-benar jelas nilai mana yang. Untuk kelas Point
, misalnya, aman dan intuitif untuk mengasumsikan bahwa nilai pertama adalah X
dan yang kedua adalah Y
, sehingga ekspresi saklar di atas adalah intuitif dan mudah dibaca.
Pola Tuple
Kasus khusus pola posisi yang sangat berguna adalah ketika diterapkan pada tupel. Jika pernyataan sakelar diterapkan ke ekspresi tupel secara langsung, kami bahkan membiarkan set kurung tambahan dihilangkan, seperti pada switch (x, y, z)
alih-alih switch ((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 bisa memilih untuk memasukkan hasKey
dalam tuple yang diaktifkan daripada menggunakan when
klausa - itu benar-benar 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 keseluruhan, saya harap Anda dapat melihat bahwa pola rekursif dan beralih ekspresi dapat mengarah pada logika program yang lebih jelas dan deklaratif.
Fitur C # 8.0 lainnya di Pratinjau 2
Sementara fitur pola adalah yang utama untuk online di VS 2019 Pratinjau 2, Ada beberapa yang lebih kecil yang saya harap Anda juga akan menemukan yang berguna dan menyenangkan. Saya tidak akan menjelaskan lebih lanjut di sini, tetapi hanya memberi Anda penjelasan singkat tentang masing-masing.
Menggunakan deklarasi
Dalam C #, using
pernyataan selalu menyebabkan tingkat bersarang, yang bisa sangat mengganggu dan merusak keterbacaan. Untuk kasus sederhana di mana Anda hanya ingin sumber daya dibersihkan di akhir ruang lingkup, Anda sekarang harus menggunakan deklarasi . Menggunakan deklarasi hanyalah deklarasi variabel lokal dengan kata kunci using
di depan, dan isinya dibuang di akhir blok pernyataan saat ini. Jadi alih-alih:
static void Main(string[] args) { using (var options = Parse(args)) { if (options["verbose"]) { WriteLine("Logging..."); } ... }
Anda cukup 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 ini bukan tempat untuk mengulangi kegunaannya, tetapi sebagai imbalannya mereka datang dengan beberapa batasan yang parah, seperti tidak dapat mengimplementasikan antarmuka. Struct Ref sekarang dapat sekali pakai tanpa menerapkan antarmuka IDisposable
, hanya dengan memiliki metode Dispose
di dalamnya.
Fungsi lokal statis
Jika Anda ingin memastikan fungsi lokal Anda tidak menimbulkan biaya runtime yang terkait dengan variabel "capturing" (referensi) dari lingkup terlampir, Anda dapat mendeklarasikannya sebagai static
. Kemudian kompiler akan mencegah referensi apa pun yang dideklarasikan dalam fungsi melampirkan - kecuali fungsi lokal statis lainnya!
Perubahan sejak Pratinjau 1
Fitur utama dari Pratinjau 1 adalah jenis referensi yang dapat dibatalkan dan aliran async. Keduanya telah berevolusi sedikit di Pratinjau 2, jadi jika Anda sudah mulai menggunakannya, berikut ini baik untuk diperhatikan.
Jenis referensi tidak dapat dibatalkan
Kami telah menambahkan lebih banyak opsi untuk mengontrol peringatan yang dapat #nullable
baik dalam sumber (melalui arahan #pragma warning
#nullable
dan #pragma warning
) dan di tingkat proyek. Kami juga mengubah keikutsertaan file proyek ke <NullableContextOptions>enable</NullableContextOptions>
.
Async stream
Kami mengubah bentuk antarmuka IAsyncEnumerable<T>
yang diharapkan oleh kompiler! Ini membuat kompiler tidak sinkron dengan antarmuka yang disediakan di .NET Core 3.0 Preview 1, yang dapat menyebabkan Anda mengalami sejumlah masalah. Namun, .NET Core 3.0 Preview 2 akan segera keluar, dan itu membawa antarmuka kembali sinkron.
Miliki itu!
Seperti biasa, kami ingin umpan balik Anda! Silakan bermain-main dengan fitur pola baru pada khususnya. Apakah Anda mengalami dinding bata? Apakah ada yang mengganggu? Apa saja skenario keren dan berguna yang Anda temukan untuk mereka? Tekan tombol umpan balik dan beri tahu kami!
Selamat melakukan peretasan
Mads Torgersen, pemimpin desain untuk C #