C# preprocessor directives là gì? Trong C# chúng ta vẫn hay thấy #if, #region, #error… đó là những preprocessor directives hay còn gọi là chỉ thị tiền xử lý. Về ý tưởng, khái niệm preprocessor trong C# là tương đồng với khái niệm reprocessor trong C/C++. Hôm nay tôi sẽ chia sẻ về preprocessor directives trong C#.
1) Định nghĩa
Preprocessor directives là một chỉ thị tiền xử lý – tức là những lệnh giúp điều hướng hoặc xác định nội dung code trong quá trình biên dịch code. Preprocessor directives trong C# có những đặc điểm sau đây:
- Nó không phải là lệnh của ngôn ngữ lập trình nên sẽ không kết thúc bằng “;“
- Khác với preprocessor directives trong C, trong C# bạn không thể dùng preprocessor directives để định nghĩa macro.
- Tất cả các preprocessor directives luôn bắt đầu bởi dấu “#“
- Các preprocessor directives phải được viết trên cùng một line
Ngữ cảnh để sử dụng preprocessor directives: Ví dụ tôi viết một ứng dụng Console khá đơn giản để phân tích nội dung một trang HTML. Chức năng chính của ứng dụng này là lấy nội dụng html từ một trang web chỉ định và phân tích nội dụng dựa trên một số tiêu chí về SEO. Trong quá trình code, dĩ nhiên sẽ có nhu cầu DEBUG hay Unit test; và chúng ta sẽ có một số khó khăn sau:
- Phụ thuộc vào kết nối internet khi lấy data từ wensite online.
- Mất nhiều thời gian cho việc tải data trong quá trình Unit test hay debug.
Vậy tôi muốn khi debug hay unit test, nội dụng cần phân tích sẽ chỉ là đọc từ một tập tin html hay từ local website là đủ. Khi thật sự là sản phẩm cuối cùng tôi mới có nhu cầu lấy data từ internet. Để làm được yêu cầu trên chúng ta sẽ nghĩ đến việc sử dụng preprocessor directives. #if DEBUG
Sau đây là một số preprocessor directives và ý nghĩa của chúng.
#define | Định nghĩa một chỉ thị/chỉ dẫn |
#undef | Huỷ một chỉ thị/chỉ dẫn nếu đã được định nghĩa trước đó |
#if | Kiểm tra một chỉ thị/chỉ định có được định nghĩa hay chưa |
#else | Block code nghịch của #if. |
#elif | Bổ xung điều kiện cho #if |
#endif | Kết thúc block code của #if |
#line | Chỉ định số thứ tự của dòng (line code) trong tập tin, thường dùng để mô tả dòng bị lỗi hay một thông điệp cho dòng code |
#error | Xuất lỗi trong quá trình complile. Mặc định số dòng là nơi bạn xuất lỗi. Có thể dùng #line để thay đổi giá trị số dòng, tập tin. |
#warning | Xuất cảnh báo trong quá trình complile. Mặc định số dòng là nơi bạn xuất lỗi. Có thể dùng #line để thay đổi giá trị số dòng, tập tin. |
#region | Đánh dấu block code |
#endregion | Kết thúc đánh dấu block code |
Các preprocessor directives được chia thành 3 nhóm.
Conditional compilation: Cho phép chỉ định định nội dung biên dịch.
- #if
- #else
- #elif
- #endif
- #define
- #undef
Errors , Warnings , Line & pragma: Cho phép thể hiện các thông báo về lỗi, cảnh báo và một số thông tin khác
- #warning
- #error
- #line
- #pragma
Region:Cho phép tạo hay đánh dấu block code
- #region
- #endregion
2) Cách dùng
#define và #undef: Chỉ được dùng ở đầu tập tin *.cs và trước mọi dòng code. #define có phạm vi chỉ trong một file.
#define VERSION_1_0_0 #undef VERSION_1_0_0 using System; namespace Preprocessor_Directives { class Program { static void Main(string[] args) { Console.WriteLine("Do something here."); } } }
Nếu dùng #define và #undef xen lẫn giữa các dòng code, bạn sẽ nhận lỗi tương tự sau:
#if, #else, #elif và #endif: Dùng để thay đổi nội dung cần biên dịch.
#define VERSION_3_0_0 #define DEBUG #define UNIT_TEST using System; namespace Preprocessor_Directives { class Program { static void Main(string[] args) { string ver = string.Empty; #if VERSION_1_0_0 ver = "1.0.0"; #elif VERSION_2_0_0 ver = "2.0.0"; #elif DEBUG || UNIT_TEST Console.WriteLine("App is running in debug mode or Unit test."); #else Console.WriteLine("Please define version."); #endif Console.WriteLine($"Version: {ver}"); } } }
Khi bạn sử dụng #if cùng với #elif, nếu điều kiện đầu tiên được thõa mản nghĩa là các điều kiện sau đó sẽ được bỏ qua nếu chúng cũng thõa mãn.
#warning and #error: Dùng để xuất cảnh báo hoặc lỗi của source code.
#define VERSION_1_0_0 #define DEBUG #define UNIT_TEST using System; namespace Preprocessor_Directives { class Program { static void Main(string[] args) { #if UNIT_TEST && DEBUG #warning "You are using Unit test in DEBUG mode." #endif #if VERSION_1_0_0 && UNIT_TEST #error "Version 1.0.0 does not allow Unit test." #endif Console.WriteLine("Do something here."); } } }
Bạn sẽ nhận được lỗi ngay trong editor khi dùng #error hay cảnh báo khi dùng #warning.
Lỗi khi build.
#line: Được dùng để thay đổi giá trị số dòng hay tên file cho việc mô tả lỗi. Ví dụ chúng ta dùng #line ở file Program để mô tả lỗi ở một file HtmlHelper.cs.
HtmlHelper.cs
#undef DEBUG using System; namespace Preprocessor_Directives { public static class HtmlHelper { public static string GetHtml(string url) { #if DEBUG return @"<html></html>"; #else throw new NotImplementedException("This function not available."); #endif } } }
Program.cs
#define VERSION_1_0_0 #undef DEBUG using System; namespace Preprocessor_Directives { class Program { static void Main(string[] args) { string html = string.Empty; #if !DEBUG #line 13 "HtmlHelper.cs" #error "Some function not available now." #endif Console.WriteLine("Do something here."); } } }
Lỗi khi build.
#pragma: Dùng để hướng dẫn trình biên dịch. Ví dụ sau đây là cách disable cảnh báo “The field “Program.html” is never used“.
Kết quả khi dùng #pragma
#region và #endregion: Dùng để gom block code.
3) Nâng cao
Chúng ta đã biết #define có phạm vi trong một tập tin *.cs thế nên nếu bạn cần sử dụng 1 chỉ thị đánh dấu cho nhiều nơi bạn sẽ phải define ở nhiều tập tin khác nhau. Đó là một nhược điểm lớn. Nhưng đôi khi bạn thấy rằng bạn không hề define cho chỉ định DEBUG nhưng bạn vẫn có thể sử dụng được ở mọi file. Đó là vì một số chỉ định được diefine trong phạm vi toàn cục ví dụ như DEBUG.
Vậy các chỉ thị toàn cục được define ở đâu. Bạn chọn project và chuột phải, chọn property.
Để define thêm các chỉ thị riêng, bạn thêm vào mục “Conditional compilation symbols”.
Hoặc mở file project để thêm.
Bài viết có tham khảo từ một số nguồn từ internet.
Phạm Tuân
Thank you for reading.
Good web site! I really love how it is simple on my eyes and the data are well written. I am wondering how I might be notified when a new post has been made. I have subscribed to your RSS which must do the trick! Have a great day!
I love the efforts you have put in this, thanks for all the great content.