
Null-Check
var product = GetProduct();
if (product == null)
{
// ...
}
var product = GetProduct();
if (product is null)
{
// ...
}
var product = GetProduct();
if (product is not null)
{
// ...
}
Reduce Nesting
Product PurchaseProduct(int id)
{
var product = GetProduct(id);
if (product.Quantity > 0)
{
product.Quantity--;
return product;
}
else
{
SendOutOfStockNotification(product);
return null;
}
}
Trong những trường hợp như vậy, ta có thể xoá những câu lệnh else và giảm cấp độ nesting một cách hiệu quả như sau:
Product PurchaseProduct(int id)
{
var product = GetProduct(id);
if (product.Quantity > 0)
{
product.Quantity--;
return product;
}
SendOutOfStockNotification(product);
return null;
}
Đôi khi, chúng ta cần đảm bảo điều kiện được đáp ứng trước khi thực hiện câu lệnh bên trong. Điều này thường dẫn đến nhiều cấp độ nesting vào nhau và mã khó đọc hơn, ví dụ như:
bool IsProductInStock(int id)
{
var product = GetProduct(id);
if (product is not null)
{
if (product.Quantity > 0)
{
return true;
}
}
return false;
}
Để giảm nesting đoạn code trên, bạn có thể làm như sau:
bool IsProductInStock(int id)
{
var product = GetProduct(id);
if (product is null)
{
return false;
}
if (product.Quantity <= 0)
{
return false;
}
return true;
}
Trên là một ví dụ về nguyên tắc early return, cụ thể là chúng ta nên trả về từ một phương thức càng sớm càng tốt. Trong trường hợp này, trước tiên ta kiểm tra xem product có null không và trả lại false nếu có. Sau đó, ta kiểm tra xem số lượng có nhỏ hơn hoặc bằng 0 hay không và trả lại false nếu đúng. Nếu không, sản phẩm không null và số lượng lớn hơn 0, vì vậy trả lại true.
bool IsProductInStock(int id)
{
var product = GetProduct(id);
if (product is null || product.Quantity <= 0)
{
return false;
}
return true;
}
Using Declarations
using (var streamReader = new StreamReader("..."))
{
string content = streamReader.ReadToEnd();
}
Từ C#8 trở lên, bạn có thể dễ dàng sử dụng câu lệnh using như sau:
using var streamReader = new StreamReader("...");
string content = streamReader.ReadToEnd();
Nhưng khi sử dụng, bạn nên lưu ý rằng câu lệnh using chỉ sử dụng ở block-level scope.
Logical Expression
C# 9 đã giới thiệu các logical patterns mới mà chúng ta có thể sử dụng để cải thiện các biểu thức logic trở nên dễ hiểu dễ đọc hơn và bớt rối hơn.
bool IsLetter(char ch) => (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
Đây là cách code điển hình sử dụng để kiểm tra một ký tự được chỉ định có phải là một chữ cái hay không, mặc dù viết hơi rườm rà vì phải lặp lại tham số cho mỗi lần kiểm tra.
bool IsLetter(char ch) => ch is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
Cách code này dễ đọc hơn nhiều vì nó gần như đọc giống như một câu. Chúng ta cũng không cần chỉ định tham số ký tự nhiều lần.
Loại bỏ if-else khi set giá trị boolean
bool IsInStock(Product product)
{
if (product.Quantity > 0)
{
return true;
}
else
{
return false;
}
}
Mặc dù cách tiếp cận này không sai, nhưng chúng ta phải tự hỏi liệu chúng ta có cần câu lệnh if ngay từ đầu hay không. Vì chúng ta đã có một biểu thức logic bên trong câu lệnh if, chúng ta có thể đơn giản hóa phương pháp này bằng cách chỉ cần trả về giá trị của biểu thức logic đó:
bool IsInStock(Product product)
{
return product.Quantity > 0;
}
Chúng ta đã code tối giản hơn nhiều để đạt được kết quả tương tự.
Chúng ta có thể đơn giản hóa thêm phương pháp trước đó bằng cách sử dụng expression body như sau:
bool IsInStock(Product product) => product.Quantity > 0;
Switch Statements
switch (DateTime.Now.DayOfWeek)
{
case DayOfWeek.Monday:
return "Not Weekend";
case DayOfWeek.Tuesday:
return "Not Weekend";
case DayOfWeek.Wednesday:
return "Not Weekend";
case DayOfWeek.Thursday:
return "Not Weekend";
case DayOfWeek.Friday:
return "Not Weekend";
case DayOfWeek.Saturday:
return "Weekend";
case DayOfWeek.Sunday:
return "Weekend";
default:
throw new ArgumentOutOfRangeException();
}
Như chúng ta thấy, chúng ta phải viết rất nhiều code và chúng lại lặp lại với nhau. Chúng ta có thể thu gọn bằng cách kết hợp tất cả các câu lệnh case để trả về cùng một kết quả:
switch (DateTime.Now.DayOfWeek)
{
case DayOfWeek.Monday:
case DayOfWeek.Tuesday:
case DayOfWeek.Wednesday:
case DayOfWeek.Thursday:
case DayOfWeek.Friday:
return "Not Weekend";
case DayOfWeek.Saturday:
case DayOfWeek.Sunday:
return "Weekend";
default:
throw new ArgumentOutOfRangeException();
}
Như trên, bạn có thể thấy code của chúng ta được tối giản đáng kể. Nhưng khi bắt đầu với C#8 trở đi, các biểu thức switch sẽ giúp chúng ta cải thiện code trở nên tối giản hơn nữa bằng cách biến đổi câu lệnh switch trước đó thành một biểu thức switch:
DateTime.Now.DayOfWeek switch
{
DayOfWeek.Monday => "Not Weekend",
DayOfWeek.Tuesday => "Not Weekend",
DayOfWeek.Wednesday => "Not Weekend",
DayOfWeek.Thursday => "Not Weekend",
DayOfWeek.Friday => "Not Weekend",
DayOfWeek.Saturday => "Weekend",
DayOfWeek.Sunday => "Weekend",
_ => throw new ArgumentOutOfRangeException()
}
Nhưng ở C#9, chúng ta cũng có thể sử dụng các logical pattern trong các biểu thức switch. Trong trường hợp này, chúng ta có thể tối giản tiếp các code trên bằng cách sử dụng or như sau:
DateTime.Now.DayOfWeek switch
{
DayOfWeek.Monday or DayOfWeek.Tuesday or DayOfWeek.Wednesday or DayOfWeek.Thursday or DayOfWeek.Friday => "Not Weekend",
DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
_ => throw new ArgumentOutOfRangeException()
}
Và tiếp theo là “trùm cuối”, giới thiệu bạn một cách tối giản hơn nữa khi sử dụng logical pattern not:
DateTime.Now.DayOfWeek switch
{
not (DayOfWeek.Saturday or DayOfWeek.Sunday) => "Not Weekend",
DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
_ => throw new ArgumentOutOfRangeException()
}
Filter Exceptions
try
{
await GetBlogsFromApi();
}
catch (HttpRequestException e)
{
if (e.StatusCode == HttpStatusCode.BadRequest)
{
HandleBadRequest(e);
}
else if (e.StatusCode == HttpStatusCode.NotFound)
{
HandleNotFound(e);
}
}
Cách viết như trên không sai, nhưng nó lại không được quá clean, và quá nhiều nesting được giới thiệu trên. Thay vì cách viết trên, bạn có thể viết như sau:
try
{
await GetBlogsFromApi();
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.BadRequest)
{
HandleBadRequest(e);
}
catch (HttpRequestException e) when (e.StatusCode == HttpStatusCode.NotFound)
{
HandleNotFound(e);
}
Return Empty Collections
IEnumerable<Product> GetProductsByCategory(string category)
{
if (string.IsNullOrWhiteSpace(category))
{
return null;
}
var products = _dbContext.Products.Where(p => p.Category == category).ToList();
return products;
}
Thực tế cách làm này không tốt vì buộc chúng ta phải gọi và kiểm tra kết quả null. Một cách tiếp cận tốt hơn sẽ là chỉ trả về một collection rỗng như sau:
IEnumerable<Product> GetProductsByCategory(string category)
{
if (string.IsNullOrWhiteSpace(category))
{
return Enumerable.Empty<Product>();
}
var products = _dbContext.Products.Where(p => p.Category == category).ToList();
return products;
}
Như bạn có thể thấy, sử dụng Array.Empty và Enumerable.Empty là cách hiệu quả để trả về một collection rỗng từ một phương thức.
Lời kết
Mong bài viết hữu ích, chúc các bạn thành công!Hieu Ho.