Ada pola dalam kelas C # yang dicontohkan oleh Dictionary.TryGetValue
dan int.TryParse
: metode yang mengembalikan boolean yang menunjukkan keberhasilan operasi dan parameter keluar yang berisi hasil aktual; jika operasi gagal, parameter keluar diatur ke nol.
Mari kita asumsikan saya menggunakan referensi non-nullable C # 8 dan ingin menulis metode TryParse untuk kelas saya sendiri. Tanda tangan yang benar adalah ini:
public static bool TryParse(string s, out MyClass? result);
Karena hasilnya nol dalam kasus palsu, variabel keluar harus ditandai sebagai nullable.
Namun, pola Coba umumnya digunakan seperti ini:
if (MyClass.TryParse(s, out var result))
{
// use result here
}
Karena saya hanya memasuki cabang ketika operasi berhasil, hasilnya tidak boleh nol di cabang itu. Tetapi karena saya menandainya sebagai nullable, sekarang saya harus memeriksa atau menggunakannya !
untuk mengganti:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // compiler warning, could be null
Console.WriteLine("Look: {0}", result!.SomeProperty); // need override
}
Ini jelek dan agak tidak ekonomis.
Karena pola penggunaan yang umum, saya memiliki pilihan lain: berbohong tentang jenis hasil:
public static bool TryParse(string s, out MyClass result) // not nullable
{
// Happy path sets result to non-null and returns true.
// Error path does this:
result = null!; // override compiler complaint
return false;
}
Sekarang penggunaan tipikal menjadi lebih baik:
if (MyClass.TryParse(s, out var result))
{
Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}
tetapi penggunaan yang tidak biasa tidak mendapatkan peringatan yang seharusnya:
else
{
Console.WriteLine("Fail: {0}", result.SomeProperty);
// Yes, result is in scope here. No, it will never be non-null.
// Yes, it will throw. No, the compiler won't warn about it.
}
Sekarang saya tidak yakin ke mana harus pergi ke sini. Apakah ada rekomendasi resmi dari tim bahasa C #? Apakah ada kode CoreFX yang telah dikonversi menjadi referensi yang tidak dapat dibatalkan yang dapat menunjukkan kepada saya bagaimana melakukan ini? (Saya pergi mencari TryParse
metode. IPAddress
Adalah kelas yang memiliki satu, tetapi belum dikonversi pada cabang utama corefx.)
Dan bagaimana kode generik suka Dictionary.TryGetValue
menangani ini? (Mungkin dengan MaybeNull
atribut khusus dari apa yang saya temukan.) Apa yang terjadi ketika saya instantiate Dictionary
dengan tipe nilai yang tidak dapat dibatalkan?
MyClass?
), dan lakukan sakelar dengan acase MyClass myObj:
dan (sebuah opsi semua)case null:
.Jawaban:
Pola bool / out-var tidak berfungsi dengan baik dengan tipe referensi yang dapat dibatalkan, seperti yang Anda gambarkan. Jadi alih-alih melawan kompiler, gunakan fitur untuk menyederhanakan banyak hal. Lempar fitur pencocokan pola yang ditingkatkan dari C # 8 dan Anda dapat memperlakukan referensi nullable sebagai "tipe orang miskin mungkin":
Dengan begitu, Anda menghindari bermain-main dengan
out
parameter dan Anda tidak perlu berkelahi dengan kompiler untuk mencampurnull
dengan referensi yang tidak dapat dibatalkan.Pada titik ini, "tipe orang miskin mungkin" jatuh. Tantangan yang akan Anda hadapi adalah ketika menggunakan nullable reference types (NRT), kompiler akan memperlakukan
Foo<T>
sebagai tidak dapat dibatalkan. Tetapi cobalah dan ubahlahFoo<T?>
dan itu akan menginginkan bahwaT
dibatasi ke kelas atau struct sebagai tipe nilai nullable adalah hal yang sangat berbeda dari sudut pandang CLR. Ada berbagai cara untuk mengatasi ini:default
(bersama dengan!
) untukout
parameter meskipun kode Anda mendaftar tanpa nol,Maybe<T>
tipe nyata sebagai nilai kembali, yang kemudian tidak pernahnull
dan membungkus itubool
danout T
ke dalamHasValue
danValue
properti atau semacamnya,Saya pribadi lebih suka menggunakan
Maybe<T>
tetapi memilikinya mendukung dekonstruksi sehingga dapat dicocokkan dengan pola sebagai tuple seperti pada 4, di atas.sumber
TryParse(someString) is {} myClass
- Sintaks ini akan membiasakan diri, tapi saya suka idenya.TryParse(someString) is var myClass
terlihat lebih mudah bagiku.x is var y
akan selalu benar, apakahx
itu nol atau tidak.Jika Anda sampai pada hal ini agak terlambat, seperti saya, ternyata tim .NET mengatasinya melalui sekelompok atribut parameter seperti
MaybeNullWhen(returnValue: true)
diSystem.Diagnostics.CodeAnalysis
ruang yang dapat Anda gunakan untuk pola coba.Sebagai contoh:
yang berarti Anda dimarahi jika Anda tidak memeriksa
true
Keterangan lebih lanjut:
sumber
Saya tidak berpikir ada konflik di sini.
keberatan Anda
aku s
Namun, pada kenyataannya tidak ada yang mencegah penugasan null ke parameter keluar dalam fungsi TryParse gaya lama.
misalnya.
Peringatan yang diberikan kepada programmer ketika mereka menggunakan parameter keluar tanpa memeriksa sudah benar. Anda harus memeriksa!
Akan ada banyak kasus di mana Anda akan dipaksa untuk mengembalikan tipe nullable di mana cabang utama dari kode mengembalikan tipe non-nullable. Peringatan itu ada di sana untuk membantu Anda membuat ini eksplisit. yaitu.
Cara non-nullable untuk kode itu akan melemparkan pengecualian di mana akan ada null. Apakah Anda menguraikan, mendapatkan atau memprioritaskan
sumber
FirstOrDefault
dapat dibandingkan, karena nolnya nilai pengembaliannya adalah sinyal utama. DalamTryParse
metode, parameter keluar tidak menjadi nol jika nilai kembali benar adalah bagian dari kontrak metode.TryParse
metode. JikaIPAddress.TryParse
pernah kembali benar tetapi tidak menetapkan non-null ke parameternya, saya akan melaporkannya sebagai bug.