Một khi đã code thì nhất định sẽ có bug, một khi có bug nhất định sẽ phải fix. Mà để fix bug thì một kĩ năng không thể thiếu đó là debug. Việc debug cũng khá là đơn giản, bạn chỉ việc đặt break point và chương trình khi chạy đến đó thì tạm ngừng, sau đó ta có thể kiểm tra từng giá trị hoặc chạy code từng dòng để kiểm tra logic đúng hay sai. Những kỹ thuật cơ bản đó chắc chắn anh em đều nắm như lòng bàn tay. Trong bài viết này, mình sẽ đề cập đến một vài kĩ thuật debug trong Visual Studio rất hữu ích tuy nhiên ít người biết đến để tăng năng suất và giảm thiểu thời gian debug.
Lưu ý nhỏ là phiên bản mình đang sử dụng là Visual Studio 2019.
Mục lục
- Prerequisites
- Debug programmatically
- Conditional breakpoints
- Immediate Window
- Edit and Continue
- Summary
Prerequisites
Để bắt đầu, mình tạo một project console app đơn giản để dễ dàng minh họa cho những gì mình sắp trình bày. Trong đoạn code phía dưới mình tạo một model Student, hàm AddSampleData để tạo ra một List<Student> gồm có 20 người một cách ngẫu nhiên, các bạn không cần quá bận tâm tới hàm này làm gì. Mình sẽ tập trung vào việc debug trong Visual Studio. Sau cùng tại hàm Main, mình sẽ in ra danh sách các sinh viên đó.
using System; using System.Collections.Generic; namespace VisualStudioDebugging { public class Student { public string Name { get; set; } public int Score { get; set; } } class Program { static void Main(string[] args) { var students = AddSampleData(); foreach (var student in students) { Console.WriteLine($"{student.Name}'s score is {student.Score}"); } Console.ReadKey(); } static List<Student> AddSampleData() { Random r = new Random(); var students = new List<Student>(); string[] firstNames = new string[10] { "Mary", "Sohpie", "Micheal", "Peter", "Wisely", "Louis", "Alex", "Hannah", "Emily", "Miley" }; string[] lastNames = new string[10] { "Rooney", "Ferguson", "Peterson", "Hoge", "Stam", "Park", "Adams", "Oliver", "Cain", "Johanson" }; for (int i = 0; i < 20; i++) { students.Add(new Student { Name = $"{firstNames[r.Next(0, 9)]} {lastNames[r.Next(0, 9)]}", Score = r.Next(10) }); } return students; } } }
Nếu các bạn run thử, Output của đoạn code trên đại khái như sau, gồm 20 dòng ứng với 20 người.
Emily Adams's score is 4
Wisely Rooney's score is 5
Emily Park's score is 0
...
Wisely Hoge's score is 8
Phần setup như vậy là xong, ta bắt đầu tìm hiểu từng kĩ thật debug.
Debug programmatically
Bình thường bạn muốn debug thì rất đơn giản muốn chương trình dừng ở đâu, bạn sẽ đặt breakpoint tại dòng đó, tuy nhiên có một cách đó là bạn có thể dừng chương trình bằng code giống Javascript. Ví dụ như sau:
... static void Main(string[] args) { var students = AddSampleData(); Debugger.Break(); foreach (var student in students) { Console.WriteLine($"{student.Name}'s score is {student.Score}"); } Console.ReadKey(); } ...
Chương trình sẽ dừng lại ở dòng Debugger.Break() mà không cần đặt breakpoint. Mặc dù chỉ khi ở chế độ debug thì chương trình mới dừng lại ở dòng đó, còn bình thường thì sẽ không chạy vào. Tuy nhiên sau khi xong việc rồi thì ta nên xóa nó đi. Một tính năng nữa cũng hữu ích của C# đó là hiển thị log ra cửa sổ Output để tiện theo dõi. Ví dụ ta muốn xem chương trình đã chạy vào hàm AddSampleData chưa và có chạy đúng mong muốn, ta sẽ thử in ra tên của sinh viên đầu tiên trong danh sách. Ta sẽ thêm Debug.WriteLine vào trước dòng trả về giá trị students.
... static List<Student> AddSampleData() { ... Debug.WriteLine("First student's name: " + students[0].Name); return students; } ...
Conditional breakpoints
Conditional breakpoints là một tính năng vô cùng cần thiết, nhất là khi ta phải làm việc với một danh sách rất dài. Trong ví dụ ta chỉ có 20 sinh viên, nhưng thử tưởng tượng ta cần dừng debug với bản ghi của sinh viên bị điểm 0 trong danh sách 1000 sinh viên, nếu ta cứ debug chạy từng dòng trong vòng for để tìm thì sẽ vô cùng tốn thời gian nếu không muốn nói là gần như bất khả thi. Thật may ta có thể đặt debug trong vòng for cùng với điều kiện như sau.
Ta đặt debug tại dòng 21 sau đó bấm chuột phải và chọn Conditions… sau đó popup màu vàng nhạt sẽ hiện ra, ta sẽ đặt điều kiện giống như trên hình. Khi đó chương trình sẽ dừng tại từng bản ghi của sinh viên có điểm số bằng 0. (Nếu bạn làm theo code của mình mà không thấy dừng ở bản ghi nào, có thể hàm random vô tình không tạo ra sinh viên nào có điểm số bằng 0, bạn hãy thử chạy lại). Trong popup này ta có thể đặt điều kiện theo Hit Count hoặc là Filter nữa, và còn một mục là Actions các bạn có thể tự tìm hiểu thêm.
Immediate Window
Giả sử bạn đang debug dở, chương trình đang chạy đến sau dòng AddSampleData() và bạn đang có một list danh sách gồm 20 sinh viên. Vì một lý do gì đó, bạn muốn thêm một người nữa vào danh sách để test, rất nhiều bạn sẽ dừng debug và quay lại sửa code. Với ví dụ trên thì không vấn đề gì, nhưng thử tưởng tượng đây là một phần của chương trình rất lớn, mỗi lần debug bạn phải build lại rất lâu hoặc bạn đã mất nhiều công sức để điền tỉ mỉ nhiều trường trong form và POST lên server, nếu phải dừng debug để quay lại điền form là nỗi kinh hoàng, đặc biệt là khi bạn phải lặp đi lặp lại thao tác này.
Mình đã từng tốn nhiều thời gian như vậy cho đến khi biết có cách để update giá trị ngay tại thời điểm debug. Đó là dùng cửa sổ Immediate Window. Nếu bạn chưa thấy cửa số này thì bạn có thể bật tại Debug -> Windows -> Immediate hoặc ấn Ctrl + Alt + I.
Nếu bạn làm theo từ đầu đến giờ thì đoạn code sẽ giống như trên, bạn ấn F5 để chương trình dừng ở dòng Debugger.Break(), lúc này biến students đã có danh sách gồm 20 người. Ở cửa sổ Immediate Window bạn chỉ việc gõ
students.Add(new Student { Name = "Bill Gates", Score = 9 })
Sau đó bạn soi lại biến students, bạn sẽ thấy bạn đã thêm thành công 1 sinh viên và danh sách hiện ra Output sẽ có là 21 người. Thực sự đây là một cách rất tiện để test dữ liệu trong lúc debug mà bạn nên biết. Khi biết được kỹ thuật này, trong từng trường hợp cụ thể, các bạn sẽ tự biết cách để áp dụng sao cho hiệu quả nhất.
Edit and Continue
Nếu như Immediate Window giúp ta sửa dữ liệu tại thời điểm debug thì chức năng Edit and Continue của Visual Studio giúp ta sửa và chạy code ngay tại thời điểm debug.
Để thử chức năng ta sẽ cập nhật logic code một chút.
... foreach (var student in students) { if (student.Score > 5) { Console.WriteLine($"{student.Name}'s score is {student.Score}, the grade is {student.Grade}"); } } ....
Với điều kiện trên, ta sẽ chỉ in ra những sinh viên có điểm lớn hơn 5. Giả sử ta đang debug đến dòng lệnh if và ta chợt muốn thử với trường hợp chỉ in ra những sinh viên có điểm lớn hơn 8. Một lần nữa, đa phần developer sẽ dừng debug và update lại code thành lớn hơn 8. Thực tế ta không cần phải làm vậy, với chương trình lớn và cần nhiều công sức để debug đến đây, việc run lại code sẽ rất mất thời gian, thay vào đó ta chỉ cần sửa trực tiếp tại thời điểm đang debug. Update câu lệnh if thành như sau
if (student.Score > 8)
sau đó Save lại và run tiếp, ta thấy output hiện ra sẽ là danh sách những sinh viên có điểm lớn hơn 8.
Lưu ý là việc Edit rồi Debug tiếp chỉ có hiệu lực với các câu lệnh logic. Khi bạn update model hay thay đổi parameter của hàm thì vẫn phải build lại nhé.
Summary
Trong bài viết này mình đã trình bày một số kỹ thuật hữu ích khi debug trong Visual Studio mà ít người để ý đến. Hy vọng giúp cho các bạn dễ dàng hơn trong việc debug, một công việc khá tốn thời gian và mệt mỏi. Tất nhiên Visual Studio còn rất nhiều kỹ thuật debug cao cấp khác như debug Nuget packet hay remote debugging, nhưng cá nhân mình thấy rất hiếm khi mình cần dùng đến, nếu bạn nào tò mò có thể Google để tiếp tục tìm hiểu thêm.