Ini adalah pertanyaan yang diajukan oleh salah satu anggota tim kami selama proses pemrograman. Karena kesalahan kompilasi ini mudah untuk dihindari, saya tidak pernah memikirkan masalah ini dengan cermat sampai saya melihat kodenya dan menyadari bahwa masalah ini tidak sesederhana itu.
Lihatlah kode ini terlebih dahulu:
kode
Program kelas
{
kekosongan statis Utama (string[] args)
{
byte[] buf = byte baru[1024];
T t = T baru();
tali str = "1234";
int n = 1234;
int?nn = 1234;
DateTime dt = DateTime.Sekarang;
objek o = 1234;
Console.WriteLine("selesai");
}
}
kelas T {} }
Menurut Anda, berapa banyak variabel yang tidak digunakan dalam kode ini?
Jika dilihat dari sudut pandang programmer, jawabannya adalah semua variabel tidak terpakai. Namun hasil yang diberikan oleh kompiler agak berlawanan dengan intuisi:
Variabel "str" telah diberi nilai, tetapi nilainya belum pernah digunakan. Variabel "n" telah diberi nilai, tetapi nilainya belum pernah digunakan nilai belum pernah digunakan.
Yang aneh adalah meskipun semua variabel dideklarasikan dengan cara yang sama, kompiler hanya menganggap beberapa di antaranya tidak digunakan. Apa yang terjadi?
Mari kita analisa satu per satu. Pertama lihat arraynya. Jika nilai default digunakan, informasi yang diberikan oleh kompiler berbeda:
byte[] buf1 = null; // Ada peringatan
byte[] buf2 = byte baru[1024]; // tidak ada peringatan
Hasil ini tampaknya menunjukkan bahwa jika penetapan parameter adalah null, kompiler tidak akan benar-benar melakukan penetapan tersebut, dan variabel akan diperlakukan seolah-olah belum digunakan. Hasil pemeriksaan IL juga dapat membuktikan pernyataan ini: untuk baris pertama, kompiler tidak menghasilkan pernyataan terkait; untuk baris kedua, instruksi newattr digunakan untuk membuat array.
Untuk kelas khusus:
T t1 = null; // Ada peringatan
T t2 = T baru(); // tidak ada peringatan
Hasil ini seharusnya dapat dimengerti (meskipun dapat dimengerti, menurut saya ini kurang baik, karena alasan yang ditunjukkan di bawah). Meskipun kita tidak memanggil metode kelas apa pun, konstruktor kelas masih dapat melakukan beberapa operasi, jadi selama kelas dibuat, kompiler akan memperlakukannya seolah-olah kelas tersebut telah digunakan.
Untuk tipe nilai dasar, perilakunya berbeda dari tipe referensi. Kompilator tidak memperlakukan penugasan awal sebagai penggunaan variabel:
int n1 = 0; // Ada peringatan
int n2 = 1234; // Ada peringatan
int?n3 = nol; // Ada peringatan
ke dalam?n4 = 0; // Ada peringatan
int?n5 = 1234; // Ada peringatan
String harus dianggap sebagai tipe referensi dalam hal implementasi, tetapi kinerjanya lebih mirip dengan tipe nilai, dan informasi peringatannya sama dengan tipe nilai.
Untuk tipe nilai yang sedikit lebih kompleks, hasilnya sedikit lebih halus:
TanggalWaktu dt1; // Ada peringatan
DateTime dt2 = new DateTime(); // Ada peringatan
DateTime dt3 = DateTime baru(2009,1,1);
DateTime dt4 = DateTime.Sekarang; // tidak ada peringatan
Ada satu hal yang perlu diperhatikan tentang hasil ini. Meskipun konstruktor default DateTime dan konstruktor berparameter keduanya merupakan konstruktor dari sudut pandang pengguna, keduanya berbeda dari sudut pandang kompiler. Dapat juga dilihat dengan mendekompilasi dengan IL bahwa jika konstruktor default dipanggil, kompiler akan memanggil instruksi initobj, sedangkan instruksi panggilan ctor digunakan untuk konstruktor berparameter. Selain itu, meskipun format kode penugasan sama persis dari sudut pandang pemrogram, kompiler akan mengadopsi strategi konstruksi yang berbeda bergantung pada nilai yang diberikan, yang juga berlawanan dengan intuisi.
Kesimpulan akhirnya sangat disesalkan, yaitu peringatan kompilasi C# tidak cukup untuk memberikan perlindungan yang memadai bagi pemrogram, terutama untuk array:
byte[] buf = byte baru[1024];
Jika Anda hanya membuat array seperti itu tanpa menggunakannya, kompiler tidak akan memberikan pesan peringatan apa pun kepada pemrogram.
Masalah lain yang patut dipertimbangkan adalah mendeklarasikan kelas tanpa menggunakan metode apa pun, seperti just
Tt = T baru()
Apakah perilaku ini masuk akal? Haruskah kompiler mengeluarkan peringatan untuk ini?
Pendapat pribadi saya adalah bahwa dari sudut pandang penggunaan, hal ini tidak masuk akal dan harus dihindari sebisa mungkin. Kompiler harus mengeluarkan peringatan jika menemukan penggunaan ini. Jika perlu, Anda dapat menghindari pesan peringatan dengan mendeklarasikannya secara khusus melalui arahan kompilasi atau metode Atribut. Namun, perilaku kompiler C# adalah tidak mengeluarkan peringatan, yang saya tidak setuju. Tentu saja, saya juga berharap semua orang bisa mengemukakan idenya masing-masing.