Setelah membaca pertanyaan ini di HNQ, saya melanjutkan membaca tentang Jenis Referensi Nullable di C # 8 , dan membuat beberapa percobaan.
Saya sangat sadar bahwa 9 kali dari 10, atau bahkan lebih sering, ketika seseorang berkata "Saya menemukan bug penyusun!" ini sebenarnya oleh desain, dan kesalahpahaman mereka sendiri. Dan karena saya mulai melihat ke fitur ini hanya hari ini, jelas saya tidak memiliki pemahaman yang sangat baik tentang itu. Dengan ini, mari kita lihat kode ini:
#nullable enable
class Program
{
static void Main()
{
var s = "";
var b = s == null; // If you comment this line out, the warning on the line below disappears
var i = s.Length; // warning CS8602: Dereference of a possibly null reference
}
}
Setelah membaca dokumentasi yang saya tautkan di atas, saya berharap s == null
kalimat itu memberi saya peringatan — setelah semua s
jelas tidak dapat dibatalkan, jadi membandingkannya dengan null
tidak masuk akal.
Alih-alih, saya mendapatkan peringatan di baris berikutnya , dan peringatan itu mengatakan bahwa s
itu mungkin merupakan referensi nol, meskipun, bagi manusia, jelas itu bukan.
Terlebih lagi, peringatan tersebut tidak ditampilkan jika kita tidak membandingkan s
untuk null
.
Saya melakukan beberapa Googling dan saya mengenai masalah GitHub , yang ternyata sepenuhnya tentang hal lain, tetapi dalam prosesnya saya melakukan percakapan dengan seorang kontributor yang memberikan beberapa wawasan lebih dalam perilaku ini (mis. "Null cheque seringkali merupakan cara yang bermanfaat. memberitahu kompiler untuk mengatur ulang kesimpulan sebelumnya tentang nullability suatu variabel. " ). Ini masih meninggalkan saya dengan pertanyaan utama yang belum terjawab.
Alih-alih menciptakan masalah GitHub baru, dan berpotensi mengambil waktu dari kontributor proyek yang sangat sibuk, saya menempatkan ini untuk komunitas.
Bisakah Anda jelaskan apa yang terjadi dan mengapa? Khususnya, mengapa tidak ada peringatan yang dihasilkan di s == null
telepon, dan mengapa kita memiliki peringatan yang CS8602
sepertinya tidak null
mungkin ada di sini? Jika inferensi nullability tidak tahan peluru, seperti yang disarankan oleh thread GitHub yang tertaut, bagaimana bisa salah? Apa yang akan menjadi contohnya?
sumber
?
karenas
tidak dapat dibatalkan. Itu tidak menjadi nullable, hanya karena kami cukup bodoh untuk membandingkannyanull
.Jawaban:
Ini secara efektif merupakan duplikat dari jawaban yang dikaitkan @stuartd, jadi saya tidak akan membahas detail yang lebih dalam di sini. Tetapi akar masalahnya adalah bahwa ini bukan bug bahasa atau bug penyusun, tetapi perilaku yang dimaksudkan persis seperti yang diterapkan. Kami melacak status nol variabel. Ketika Anda awalnya mendeklarasikan variabel, status itu adalah NotNull karena Anda secara eksplisit menginisialisasi dengan nilai yang tidak nol. Tapi kami tidak melacak dari mana NotNull itu berasal. Ini, misalnya, adalah kode yang secara efektif setara:
Dalam kedua kasus, Anda secara eksplisit menguji
s
untuknull
. Kami mengambil ini sebagai masukan untuk analisis aliran, seperti yang dijawab Mads dalam pertanyaan ini: https://stackoverflow.com/a/59328672/2672518 . Dalam jawaban itu, hasilnya adalah Anda mendapatkan peringatan saat kembali. Dalam hal ini, jawabannya adalah bahwa Anda mendapatkan peringatan bahwa Anda mendereferensi referensi yang mungkin nol.Yap, sebenarnya. Ke kompiler. Sebagai manusia, kita dapat melihat kode ini dan jelas memahami bahwa itu tidak dapat membuang pengecualian referensi nol. Tetapi cara analisis aliran nullable diimplementasikan dalam kompiler, tidak bisa. Kami memang membahas sejumlah peningkatan pada analisis ini di mana kami menambahkan status tambahan berdasarkan dari mana nilai itu berasal, tetapi kami memutuskan bahwa ini menambahkan banyak kerumitan pada implementasi karena bukan banyak keuntungan, karena satu-satunya tempat di mana ini akan berguna untuk kasus-kasus seperti ini, di mana pengguna menginisialisasi variabel dengan
new
nilai konstan atau dan kemudian memeriksanyanull
.sumber
s == null
tidak menghasilkan peringatan?#nullable enable
;string s = "";s = null;
mengkompilasi dan bekerja (masih menghasilkan peringatan) apa manfaatnya, dari implementasi yang memungkinkan penetapan nol ke "referensi yang tidak dapat dibatalkan" dalam konteks anotasi nol yang diaktifkan?s == null
. Mungkin, misalnya, Anda menggunakan metode publik, dan Anda ingin melakukan validasi parameter. Atau, mungkin Anda menggunakan pustaka yang beranotasi tidak benar, dan sampai mereka memperbaiki bug itu Anda harus berurusan dengan nol di mana tidak dinyatakan. Dalam salah satu dari kasus-kasus itu, jika kami memperingatkan, itu akan menjadi pengalaman yang buruk. Sedangkan untuk memungkinkan penugasan: anotasi variabel lokal hanya untuk membaca. Mereka tidak memengaruhi runtime sama sekali. Faktanya, kami menempatkan semua peringatan itu dalam satu kode kesalahan sehingga Anda dapat mematikannya jika Anda ingin mengurangi churn kode.Saya dengan senang hati mengadopsi referensi nullable dari C # 8 segera setelah tersedia. Karena saya terbiasa menggunakan notasi [NotNull] (dll) ReSharper, saya memang melihat beberapa perbedaan antara keduanya.
Kompiler C # dapat dikelabui, tetapi cenderung salah di sisi hati-hati (biasanya, tidak selalu).
Sebagai referensi untuk pengunjung masa depan, ini adalah skenario yang saya lihat membuat kompiler bingung (saya menganggap semua kasus ini sesuai desain):
Namun, hal itu memungkinkan untuk menggunakan beberapa konstruk untuk memeriksa nullability yang juga menghilangkan peringatan:
atau atribut (masih sangat keren) (temukan semuanya di sini ):
string?
hanya string,int?
menjadiNullable<int>
dan memaksa kompiler untuk menanganinya dengan cara yang sangat berbeda. Juga di sini, kompiler memilih jalur aman, memaksa Anda untuk menentukan apa yang Anda harapkan:Menyelesaikan kendala pemberian:
Tetapi jika kita tidak menggunakan batasan dan menghapus '?' dari Data, kami masih dapat memasukkan nilai nol di dalamnya menggunakan kata kunci 'default':
Yang terakhir tampaknya lebih sulit bagi saya, karena memang memungkinkan untuk menulis kode yang tidak aman.
Semoga ini bisa membantu seseorang.
sumber
ThrowIfNull(s);
meyakinkan saya bahwas
tidak nol), tidak ada. Juga artikel menjelaskan cara Handler non-nullable obat generik, sementara aku menunjukkan bagaimana Anda dapat "menipu" compiler, memiliki nilai nol tetapi tidak ada peringatan tentang hal itu.DoesNotReturnIf(bool)
.DoesNotReturnIfNull(nullable)
.