Teknik dan alat penelusuran kesalahan untuk membantu Anda menulis kode yang lebih baik
Memperbaiki bug dan kesalahan dalam kode Anda dapat menjadi tugas yang memakan waktu dan terkadang membuat frustrasi. Dibutuhkan waktu untuk mempelajari cara men-debug secara efektif. IDE yang kuat seperti Visual Studio dapat membuat pekerjaan Anda jauh lebih mudah. IDE dapat membantu Anda memperbaiki kesalahan dan men-debug kode Anda dengan lebih cepat, dan membantu Anda menulis kode yang lebih baik dengan lebih sedikit bug. Artikel ini menyediakan tampilan holistik dari proses "perbaikan bug", sehingga Anda dapat mengetahui kapan harus menggunakan penganalisis kode, kapan menggunakan debugger, cara memperbaiki pengecualian, dan cara membuat kode untuk niat. Jika Anda sudah tahu bahwa Anda perlu menggunakan debugger, lihat Pertama-tama lihat debugger.
Dalam artikel ini, Anda mempelajari cara bekerja dengan IDE untuk membuat sesi pengkodian Anda lebih produktif. Kami membahas beberapa tugas, seperti:
Siapkan kode Anda untuk penelusuran kesalahan dengan menggunakan penganalisis kode IDE
Cara memperbaiki pengecualian (kesalahan run-time)
Cara meminimalkan bug dengan mengodekan niat (menggunakan penegasan)
Waktu menggunakan debugger
Untuk menunjukkan tugas-tugas ini, kami menunjukkan beberapa jenis kesalahan dan bug paling umum yang mungkin Anda temui saat mencoba men-debug aplikasi Anda. Meskipun kode sampel adalah C#, informasi konseptual biasanya berlaku untuk C++, Visual Basic, JavaScript, dan bahasa lain yang didukung oleh Visual Studio (kecuali jika disebutkan). Cuplikan layar berada di C#.
Membuat aplikasi sampel dengan beberapa bug dan kesalahan di dalamnya
Kode berikut memiliki beberapa bug yang dapat Anda perbaiki menggunakan IDE Visual Studio. Aplikasi ini adalah aplikasi sederhana yang mensimulasikan mendapatkan data JSON dari beberapa operasi, mendeserialisasi data ke objek, dan memperbarui daftar sederhana dengan data baru.
Untuk membuat aplikasi, Anda harus menginstal Visual Studio dan beban kerja pengembangan desktop .NET terinstal.
Jika Anda belum menginstal Visual Studio, buka halaman pengunduhan Visual Studio untuk menginstalnya secara gratis.
Jika Anda perlu menginstal beban kerja tetapi sudah memiliki Visual Studio, pilih Alat>Dapatkan Alat dan Fitur. Alat Penginstal Visual Studio diluncurkan. Pilih beban kerja Pengembangan desktop .NET, lalu pilih Ubah.
Ikuti langkah-langkah berikut untuk membuat aplikasi:
Buka Visual Studio. Di jendela mulai, pilih Buat proyek baru.
Di kotak pencarian, masukkan konsol lalu salah satu opsi Aplikasi Konsol untuk .NET.
Pilih Selanjutnya.
Masukkan nama proyek seperti Console_Parse_JSON, lalu pilih Berikutnya atau Buat, sebagaimana berlaku.
Pilih kerangka kerja target yang direkomendasikan atau .NET 8, lalu pilih Buat.
Jika Anda tidak melihat templat proyek Aplikasi Konsol untuk .NET, buka Alat>Dapatkan Alat dan Fitur, yang membuka Alat Penginstal Visual Studio. Pilih beban kerja Pengembangan desktop .NET, lalu pilih Ubah.
Visual Studio membuat proyek konsol, yang muncul di Penjelajah Solusi di panel kanan.
Saat proyek siap, ganti kode default dalam file Program.cs proyek dengan kode sampel berikut:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;
namespace Console_Parse_JSON
{
class Program
{
static void Main(string[] args)
{
var localDB = LoadRecords();
string data = GetJsonData();
User[] users = ReadToObject(data);
UpdateRecords(localDB, users);
for (int i = 0; i < users.Length; i++)
{
List<User> result = localDB.FindAll(delegate (User u) {
return u.lastname == users[i].lastname;
});
foreach (var item in result)
{
Console.WriteLine($"Matching Record, got name={item.firstname}, lastname={item.lastname}, age={item.totalpoints}");
}
}
Console.ReadKey();
}
// Deserialize a JSON stream to a User object.
public static User[] ReadToObject(string json)
{
User deserializedUser = new User();
User[] users = { };
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
DataContractJsonSerializer ser = new DataContractJsonSerializer(users.GetType());
users = ser.ReadObject(ms) as User[];
ms.Close();
return users;
}
// Simulated operation that returns JSON data.
public static string GetJsonData()
{
string str = "[{ \"points\":4o,\"firstname\":\"Fred\",\"lastname\":\"Smith\"},{\"lastName\":\"Jackson\"}]";
return str;
}
public static List<User> LoadRecords()
{
var db = new List<User> { };
User user1 = new User();
user1.firstname = "Joe";
user1.lastname = "Smith";
user1.totalpoints = 41;
db.Add(user1);
User user2 = new User();
user2.firstname = "Pete";
user2.lastname = "Peterson";
user2.totalpoints = 30;
db.Add(user2);
return db;
}
public static void UpdateRecords(List<User> db, User[] users)
{
bool existingUser = false;
for (int i = 0; i < users.Length; i++)
{
foreach (var item in db)
{
if (item.lastname == users[i].lastname && item.firstname == users[i].firstname)
{
existingUser = true;
item.totalpoints += users[i].points;
}
}
if (existingUser == false)
{
User user = new User();
user.firstname = users[i].firstname;
user.lastname = users[i].lastname;
user.totalpoints = users[i].points;
db.Add(user);
}
}
}
}
[DataContract]
internal class User
{
[DataMember]
internal string firstname;
[DataMember]
internal string lastname;
[DataMember]
// internal double points;
internal string points;
[DataMember]
internal int totalpoints;
}
}
Menemukan garis berlekuk merah dan hijau!
Sebelum Anda mencoba memulai aplikasi sampel dan menjalankan debugger, periksa kode di editor kode untuk garis berlekuk merah dan hijau. Ini mewakili kesalahan dan peringatan yang diidentifikasi oleh penganalisis kode IDE. Garis berlekuk merah adalah kesalahan waktu kompilasi, yang harus Anda perbaiki sebelum Anda dapat menjalankan kode. Garis berlekuk hijau adalah peringatan. Meskipun Anda sering dapat menjalankan aplikasi tanpa memperbaiki peringatan, aplikasi tersebut dapat menjadi sumber bug dan Anda sering menghemat waktu dan masalah dengan menyelidikinya. Peringatan dan kesalahan ini juga muncul di jendela Daftar Kesalahan, jika Anda lebih suka tampilan daftar.
Di aplikasi sampel, Anda akan melihat beberapa squiggle merah yang perlu Anda perbaiki, dan yang hijau yang perlu Anda selidiki. Berikut adalah kesalahan pertama.
Untuk memperbaiki kesalahan ini, Anda dapat melihat fitur lain dari IDE, yang diwakili oleh ikon bola lampu.
Periksa bola lampu!
Garis berlekuk merah pertama mewakili kesalahan waktu kompilasi. Arahkan kursor ke atasnya dan Anda melihat pesan The name `Encoding` does not exist in the current context
.
Perhatikan bahwa kesalahan ini menunjukkan ikon bola lampu di kiri bawah. Bersama dengan ikon obeng , ikon bola lampu mewakili Tindakan Cepat yang dapat membantu Anda memperbaiki atau merefaktor kode sebaris. Bola lampu mewakili masalah yang harus Anda perbaiki. Obeng adalah untuk masalah yang mungkin Anda pilih untuk diperbaiki. Gunakan perbaikan yang disarankan pertama untuk mengatasi kesalahan ini dengan mengeklik menggunakan System.Text di sebelah kiri.
Saat Anda memilih item ini, Visual Studio menambahkan using System.Text
pernyataan di bagian atas file Program.cs , dan squiggle merah menghilang. (Saat Anda tidak yakin tentang perubahan yang diterapkan oleh perbaikan yang disarankan, pilih Pratinjau tautan perubahan di sebelah kanan sebelum menerapkan perbaikan.)
Kesalahan sebelumnya adalah kesalahan umum yang biasanya Anda perbaiki dengan menambahkan pernyataan using
baru ke kode Anda. Ada beberapa kesalahan umum dan serupa dengan yang satu ini seperti The type or namespace "Name" cannot be found.
Jenis kesalahan ini mungkin menunjukkan referensi rakitan yang hilang (klik kanan proyek, pilih Tambahkan>Referensi), nama yang salah eja, atau pustaka yang hilang yang perlu Anda tambahkan (untuk C#, klik kanan proyek dan pilih Kelola Paket NuGet).
Memperbaiki kesalahan dan peringatan yang tersisa
Ada beberapa garis berlekuk lagi untuk dilihat dalam kode ini. Di sini, Anda akan melihat kesalahan konversi jenis umum. Saat mengarahkan kuarter ke atas squiggle, Anda melihat bahwa kode mencoba mengonversi string ke int, yang tidak didukung kecuali Anda menambahkan kode eksplisit untuk melakukan konversi.
Karena penganalisis kode tidak dapat menebak niat Anda, tidak ada bola lampu untuk membantu Anda saat ini. Untuk memperbaiki kesalahan ini, Anda perlu mengetahui niat kode. Dalam contoh ini, tidak terlalu sulit untuk melihat bahwa points
harus menjadi nilai numerik (bilangan bulat), karena Anda mencoba menambahkan points
ke totalpoints
.
Untuk memperbaiki kesalahan ini, ubah anggota points
dari kelas User
dari ini:
[DataMember]
internal string points;
ke berikut ini:
[DataMember]
internal int points;
Garis berlekuk merah di editor kode hilang.
Selanjutnya, arahkan kursor ke atas garis berlekuk hijau dalam deklarasi anggota data points
. Penganalisis kode memberi tahu Anda bahwa variabel tidak pernah diberi nilai.
Biasanya, ini mewakili masalah yang perlu diperbaiki. Namun, di aplikasi sampel, Anda sebenarnya menyimpan data dalam variabel points
selama proses deserialisasi, lalu menambahkan nilai tersebut ke anggota data totalpoints
. Dalam contoh ini, Anda mengetahui niat kode dan dapat mengabaikan peringatan dengan aman. Namun, jika Anda ingin menghilangkan peringatan tersebut, Anda dapat mengganti kode berikut:
item.totalpoints = users[i].points;
dengan ini:
item.points = users[i].points;
item.totalpoints += users[i].points;
Garis berlekuk hijau hilang.
Memperbaiki pengecualian
Ketika Anda telah memperbaiki semua berlekuk merah dan diselesaikan--atau setidaknya diselidiki--semua berlekuk hijau, Anda siap untuk memulai debugger dan menjalankan aplikasi.
Tekan F5 (Debug > Mulai Debugging) atau tombol Mulai Penelusuran Kesalahan di toolbar Debug.
Pada titik ini, aplikasi sampel menampilkan pengecualian SerializationException
(kesalahan runtime). Artinya, aplikasi tersedak pada data yang coba diserialisasikan. Karena Anda memulai aplikasi dalam mode debug (debugger terlampir), Pembantu Pengecualian debugger membawa Anda langsung ke kode yang menampilkan pengecualian dan memberi Anda pesan kesalahan yang bermanfaat.
Pesan kesalahan menginstruksikan Anda bahwa nilai 4o
tidak dapat diurai sebagai bilangan bulat. Jadi, dalam contoh ini, Anda tahu datanya buruk: 4o
harus 40
. Namun, jika Anda tidak mengontrol data dalam skenario nyata (katakanlah Anda mendapatkannya dari layanan web), apa yang Anda lakukan tentang hal itu? Bagaimana Anda memperbaikinya?
Ketika Anda mencapai pengecualian, Anda perlu mengajukan (dan menjawab) beberapa pertanyaan:
Apakah pengecualian ini hanya bug yang dapat Anda perbaiki? Atau,
Apakah pengecualian ini sesuatu yang mungkin ditemui pengguna Anda?
Jika itu pertanyaan yang pertama, perbaiki bug. (Dalam aplikasi sampel, maka Anda perlu memperbaiki data yang buruk.) Jika yang terakhir, Anda mungkin perlu menangani pengecualian dalam kode Anda menggunakan try/catch
blok (kita melihat kemungkinan strategi lain di bagian berikutnya). Di aplikasi sampel, ganti kode berikut:
users = ser.ReadObject(ms) as User[];
dengan kode ini:
try
{
users = ser.ReadObject(ms) as User[];
}
catch (SerializationException)
{
Console.WriteLine("Give user some info or instructions, if necessary");
// Take appropriate action for your app
}
Blok try/catch
memiliki beberapa biaya performa, jadi Anda hanya ingin menggunakannya ketika Anda benar-benar membutuhkannya, yaitu, di mana (a) mereka mungkin terjadi dalam versi rilis aplikasi, dan di mana (b) dokumentasi untuk metode menunjukkan bahwa Anda harus memeriksa pengecualian (dengan asumsi dokumentasinya selesai!). Dalam banyak kasus, Anda dapat menangani pengecualian dengan tepat dan pengguna tidak perlu mengetahuinya.
Berikut adalah beberapa tips penting untuk penanganan pengecualian:
Hindari menggunakan blok tangkapan kosong, seperti
catch (Exception) {}
, yang tidak mengambil tindakan yang sesuai untuk mengekspos atau menangani kesalahan. Blok tangkapan kosong atau noninformatif dapat menyembunyikan pengecualian dan dapat membuat kode Anda lebih sulit untuk di-debug alih-alih lebih mudah.Gunakan blok
try/catch
di sekitar fungsi tertentu yang menampilkan pengecualian (ReadObject
, di aplikasi sampel). Jika Anda menggunakannya di sekitar potongan kode yang lebih besar, Anda akhirnya menyembunyikan lokasi kesalahan. Misalnya, jangan gunakan bloktry/catch
di sekitar panggilan ke fungsi indukReadToObject
, yang ditunjukkan di sini, atau Anda tidak akan tahu persis di mana pengecualian terjadi.// Don't do this try { User[] users = ReadToObject(data); } catch (SerializationException) { }
Untuk fungsi yang tidak dikenal yang Anda sertakan dalam aplikasi, terutama fungsi yang berinteraksi dengan data eksternal (seperti permintaan web), periksa dokumentasi untuk melihat pengecualian apa yang mungkin dilemparkan fungsi. Ini bisa menjadi informasi penting untuk penanganan kesalahan yang tepat dan untuk penelusuran kesalahan aplikasi Anda.
Untuk aplikasi sampel, perbaiki SerializationException
dalam metode GetJsonData
dengan mengubah 4o
ke 40
.
Tip
Jika Anda memiliki Copilot, Anda bisa mendapatkan bantuan AI saat menelusuri kesalahan pengecualian. Cukup cari tombol TanyaKan Salinan. Untuk informasi selengkapnya, lihat Debug dengan Copilot.
Mengklarifikasi niat kode Anda menggunakan penegasan
Pilih tombol Mulai Ulang di Toolbar Debug (Ctrl + Shift + F5). Ini menghidupkan ulang aplikasi dalam langkah yang lebih sedikit. Anda melihat output berikut di jendela konsol.
Anda dapat melihat sesuatu dalam output ini tidak benar. Nama dan nilai nama belakang untuk rekaman ketiga kosong!
Ini adalah saat yang tepat untuk berbicara tentang praktik pengodean yang bermanfaat, sering kali kurang dimanfaatkan, yaitu menggunakan pernyataan assert
dalam fungsi Anda. Dengan menambahkan kode berikut, Anda menyertakan pemeriksaan runtime untuk memastikan bahwa firstname
dan lastname
bukan null
. Ganti metode UpdateRecords
dengan kode berikut:
if (existingUser == false)
{
User user = new User();
user.firstname = users[i].firstname;
user.lastname = users[i].lastname;
dengan ini:
// Also, add a using statement for System.Diagnostics at the start of the file.
Debug.Assert(users[i].firstname != null);
Debug.Assert(users[i].lastname != null);
if (existingUser == false)
{
User user = new User();
user.firstname = users[i].firstname;
user.lastname = users[i].lastname;
Dengan menambahkan pernyataan assert
seperti ini ke fungsi Anda selama proses pengembangan, Anda dapat membantu menentukan niat kode Anda. Dalam contoh sebelumnya, kami menentukan item berikut:
- String yang valid diperlukan untuk nama depan
- String yang valid diperlukan untuk nama belakang
Dengan menentukan niat dengan cara ini, Anda menerapkan persyaratan Anda. Ini adalah metode sederhana dan berguna yang dapat Anda gunakan untuk memunculkan bug selama pengembangan. (Pernyataan assert
juga digunakan sebagai elemen utama dalam pengujian unit.)
Pilih tombol Mulai Ulang di Toolbar Debug (Ctrl + Shift + F5).
Catatan
Kode assert
hanya aktif dalam build Debug.
Saat Anda menghidupkan ulang, debugger berhenti sementara pada pernyataan assert
, karena ekspresi users[i].firstname != null
mengevaluasi ke false
alih-alih true
.
Kesalahan assert
ini memberi tahu Anda bahwa ada masalah yang perlu Anda selidiki. assert
dapat mencakup banyak skenario di mana Anda tidak selalu melihat pengecualian. Dalam contoh ini, pengguna tidak melihat pengecualian, dan null
nilai ditambahkan seperti firstname
dalam daftar catatan Anda. Kondisi ini dapat menyebabkan masalah di kemudian hari (seperti yang Anda lihat di output konsol) dan mungkin lebih sulit untuk di-debug.
Catatan
Dalam skenario di mana Anda memanggil metode pada nilai null
, menghasilkan NullReferenceException
. Anda biasanya ingin menghindari penggunaan blok try/catch
untuk pengecualian umum, yaitu pengecualian yang tidak terkait dengan fungsi pustaka tertentu. Objek apa pun dapat menampilkan NullReferenceException
. Periksa dokumentasi untuk fungsi pustaka jika Anda tidak yakin.
Selama proses penelusuran kesalahan, ada baiknya untuk menyimpan pernyataan assert
tertentu sampai Anda tahu bahwa Anda perlu menggantinya dengan perbaikan kode aktual. Katakanlah Anda memutuskan bahwa pengguna mungkin mengalami pengecualian dalam build rilis aplikasi. Dalam hal ini, Anda harus memfaktorkan ulang kode untuk memastikan bahwa aplikasi Anda tidak menampilkan pengecualian fatal atau mengakibatkan beberapa kesalahan lainnya. Jadi, untuk memperbaiki kode ini, ganti kode berikut:
if (existingUser == false)
{
User user = new User();
dengan kode ini:
if (existingUser == false && users[i].firstname != null && users[i].lastname != null)
{
User user = new User();
Dengan menggunakan kode ini, Anda memenuhi persyaratan kode dan memastikan bahwa rekaman dengan firstname
nilai atau lastname
null
tidak ditambahkan ke data.
Dalam contoh ini, kami menambahkan dua pernyataan assert
di dalam perulangan. Biasanya, saat menggunakan assert
, yang terbaik adalah menambahkan pernyataan assert
di titik masuk (awal) fungsi atau metode. Saat ini Anda sedang melihat UpdateRecords
metode di aplikasi sampel. Dalam metode ini, Anda tahu bahwa Anda berada dalam masalah jika salah satu argumen metode adalah null
, jadi periksa keduanya dengan pernyataan assert
di titik masuk fungsi.
public static void UpdateRecords(List<User> db, User[] users)
{
Debug.Assert(db != null);
Debug.Assert(users != null);
Untuk pernyataan sebelumnya, niat Anda adalah memuat data yang ada (db
) dan mengambil data baru (users
) sebelum memperbarui apa pun.
Anda dapat menggunakan assert
dengan segala jenis ekspresi yang memutuskan ke true
atau false
. Jadi, misalnya, Anda dapat menambahkan pernyataan assert
seperti ini.
Debug.Assert(users[0].points > 0);
Kode sebelumnya berguna jika Anda ingin menentukan niat berikut: nilai titik baru yang lebih besar dari nol (0) diperlukan untuk memperbarui catatan pengguna.
Memeriksa kode Anda di debugger
Oke, sekarang setelah Anda memperbaiki segala sesuatu yang penting yang salah dengan aplikasi sampel, Anda dapat beralih ke hal-hal penting lainnya!
Kami menunjukkan Pembantu Pengecualian debugger kepada Anda, tetapi debugger adalah alat yang jauh lebih kuat yang juga memungkinkan Anda melakukan hal-hal lain seperti menelusuri kode Anda dan memeriksa variabelnya. Kemampuan yang lebih kuat ini berguna dalam banyak skenario, terutama skenario berikut:
Anda mencoba mengisolasi bug runtime dalam kode Anda, tetapi tidak dapat melakukannya menggunakan metode dan alat yang sebelumnya dibahas.
Anda ingin memvalidasi kode Anda, yaitu, menontonnya saat berjalan untuk memastikannya berperilaku dengan cara yang Anda harapkan dan melakukan apa yang Anda inginkan.
Sangat instruktif untuk menonton kode Anda saat berjalan. Anda dapat mempelajari selengkapnya tentang kode Anda dengan cara ini dan sering kali dapat mengidentifikasi bug sebelum mereka menunjukkan gejala yang jelas.
Untuk mempelajari cara menggunakan fitur penting debugger, lihat Penelusuran kesalahan untuk pemula absolut.
Memperbaiki masalah performa
Jenis bug lainnya termasuk kode yang tidak efisien yang menyebabkan aplikasi Anda berjalan lambat atau menggunakan terlalu banyak memori. Umumnya, mengoptimalkan performa adalah sesuatu yang Anda lakukan nanti dalam pengembangan aplikasi Anda. Namun, Anda dapat mengalami masalah performa lebih awal (misalnya, Anda melihat bahwa beberapa bagian aplikasi Berjalan lambat), dan Anda mungkin perlu menguji aplikasi dengan alat pembuatan profil sejak dini. Untuk informasi selengkapnya tentang alat pembuatan profil seperti alat Penggunaan CPU dan Penganalisis Memori, lihat Pertama-tama lihat alat pembuatan profil.
Konten terkait
Dalam artikel ini, Anda telah mempelajari cara menghindari dan memperbaiki banyak bug umum dalam kode Anda dan kapan harus menggunakan debugger. Selanjutnya, pelajari selengkapnya tentang menggunakan debugger Visual Studio untuk memperbaiki bug.