1. Bộ sưu tập OrderedDictionary
.NET luôn nổi tiếng với thư viện chuẩn phong phú, nhưng dân dev vẫn hay gặp cảnh phải thỏa hiệp: muốn dictionary mà nhớ thứ tự thêm phần tử, hoặc cần một tập hợp giá trị duy nhất không đổi. Trước .NET 9 thì phải xài thư viện ngoài hoặc tự chế (có người còn lên GitHub copy OrderedDictionary về — shhh, không ai biết đâu).
Từ .NET 9 thì có bộ sưu tập mới xịn sò — khỏi cần hack nữa! Cùng soi kỹ hai món hữu ích nhất: OrderedDictionary<TKey, TValue> và ReadOnlySet<T>.
1. OrderedDictionary là gì?
OrderedDictionary là kiểu lai giữa dictionary và list. Nó lưu cặp "khóa-giá trị" như Dictionary<TKey, TValue> bình thường, nhưng đảm bảo thứ tự phần tử đúng như lúc bạn thêm vào. Cực kỳ quan trọng khi cần duyệt phần tử đúng thứ tự user nhập, hoặc khi thứ tự ảnh hưởng logic nghiệp vụ: in báo cáo, serialize data, sinh file config.
So sánh vuiDictionary thường thì như cái tủ nhiều ngăn, muốn nhét/lấy gì cũng được, chẳng quan tâm thứ tự. Còn OrderedDictionary thì như tủ kéo gọn gàng, mọi thứ nằm đúng chỗ, bạn luôn biết cái gì vào trước, cái gì sau.
2. Khác biệt chính với Dictionary
- Dictionary: không đảm bảo thứ tự phần tử (dù có vẻ ổn — đừng tin, chỉ là ảo giác thôi!).
- OrderedDictionary: phần tử lưu đúng thứ tự bạn thêm vào.
3. Cú pháp và các method cơ bản
Ví dụ đơn giản:
using System.Collections.Generic;
var od = new OrderedDictionary<string, int>();
od.Add("Ivan", 5);
od.Add("Svetlana", 8);
od.Add("Alex", 3);
// Duyệt đúng thứ tự thêm:
foreach (var pair in od)
{
Console.WriteLine($"{pair.Key}: {pair.Value}");
}
Kết quả:
Ivan: 5
Svetlana: 8
Alex: 3
Nếu thử ví dụ này với dictionary thường, thứ tự sẽ khác. Còn OrderedDictionary thì luôn đúng thứ tự bạn thêm.
OrderedDictionary cũng implement các interface như dictionary thường:
- IDictionary<TKey, TValue>
- IReadOnlyDictionary<TKey, TValue>
- IEnumerable<KeyValuePair<TKey, TValue>>
4. Truy cập theo index và theo key
Điểm hay: OrderedDictionary có indexer theo key và theo index!
// Theo tên (key):
int svetlanaScore = od["Svetlana"]; // 8
// Theo index:
var firstEntry = od.ElementAt(0); // KeyValuePair<string, int>("Ivan", 5)
5. Update và xóa
Nếu bạn thêm phần tử mới với key đã có, sẽ bị exception. Muốn thay đổi value — dùng indexer:
od["Ivan"] = 10; // Thay đổi value, thứ tự không đổi
Xóa được cả theo key lẫn index:
od.Remove("Svetlana");
od.RemoveAt(0); // xóa "Ivan"
6. Minh họa cấu trúc
flowchart LR
A("0: Ivan - 5")
B("1: Svetlana - 8")
C("2: Alex - 3")
A --> B --> C
7. Lưu ý và lỗi thường gặp
Nhiều dev cố dùng Dictionary<TKey, TValue> và hy vọng thứ tự ổn — nhưng đó là bẫy! Có thể lúc test thì đúng, lần sau lại khác (nhất là khi .NET update hoặc chạy trên nền tảng khác).
OrderedDictionary do phải lưu thứ tự nên một số thao tác (thêm, xóa giữa chừng) hơi chậm hơn dictionary thường, nhưng đa số trường hợp thì lợi ích vẫn lớn hơn.
Nếu bạn hay cần truy cập theo index — dùng OrderedDictionary, còn chỉ cần tìm nhanh theo key, không quan tâm thứ tự — cứ xài dictionary thường.
8. Ví dụ thực tế cho app
Giả sử app quản lý nhân viên và phòng ban cần báo cáo, nhân viên in ra đúng thứ tự thêm:
var employeeScores = new OrderedDictionary<string, int>();
employeeScores.Add("Петр", 100);
employeeScores.Add("Анна", 150);
employeeScores.Add("Виктория", 80);
// Báo cáo luôn đúng thứ tự:
foreach (var pair in employeeScores)
{
Console.WriteLine($"{pair.Key}: {pair.Value} баллов");
}
2. Bộ sưu tập ReadOnlySet<T>
1. ReadOnlySet là gì?
ReadOnlySet<T> là tập hợp (set) không thể thay đổi (immutable) gồm các giá trị duy nhất. Trước đây, muốn có set chỉ đọc thì phải trả về bản copy qua .ToHashSet() hoặc tự bọc lại. Giờ có bộ sưu tập này, không thể thêm/xóa sau khi tạo. Giúp code an toàn hơn, tránh lỗi do ai đó lỡ tay sửa data bên ngoài.
So sánh vuiKiểu như bạn ghi tên duy nhất vào sổ rồi... bọc plastic lại. Không ai thêm, xé hay sửa được nữa.
2. Tạo ReadOnlySet
Cách đơn giản nhất là dùng extension method:
var colors = new[] { "Red", "Green", "Blue", "Green" };
var readOnlyColors = colors.ToReadOnlySet();
// Chỉ có giá trị duy nhất, không thể sửa:
foreach (var color in readOnlyColors)
Console.WriteLine(color);
Kết quả:
Red
Green
Blue
Cũng có thể dùng constructor trực tiếp (nếu cần):
var set = new ReadOnlySet<int>(new[] {1, 2, 2, 3, 5, 1}); // Vẫn chỉ có 1,2,3,5
ReadOnlySet trên Microsoft Docs
3. Tính chất và method chính
- Chỉ đọc (immutable)
- Count — số lượng phần tử
- Contains(item) — kiểm tra có phần tử không
- Hỗ trợ LINQ (IEnumerable<T>)
- Tìm kiếm nhanh, không hỗ trợ thêm/xóa/clear
Ví dụ:
if (readOnlyColors.Contains("Red"))
Console.WriteLine("Có màu đỏ trong danh sách!");
Không thể làm thế này:
// Lỗi compile!
readOnlyColors.Add("Yellow");
4. Khi nào dùng?
Rất hay khi bạn muốn trả ra set cho bên ngoài, nhưng không muốn ai đó vô tình (hoặc cố ý) sửa nó. Ví dụ:
- Trả về danh sách role user được hỗ trợ
- Danh sách extension file hợp lệ
- Danh sách tham số config không được phép sửa
Ví dụ trong app: giả sử có danh sách phòng ban hợp lệ:
public static ReadOnlySet<string> Departments { get; } =
new[] { "Кадры", "Разработка", "Бухгалтерия" }.ToReadOnlySet();
Không ai có thể thay đổi danh sách phòng ban từ bên ngoài.
5. Minh họa: khác biệt với HashSet thường
graph LR
A[HashSet] -- Add/Remove/Contains --> B((Elements))
C[ReadOnlySet] -- Only Contains --> D((Elements))
3. OrderedDictionary vs. ReadOnlySet: bảng so sánh
| Bộ sưu tập | Lưu cặp? | Đảm bảo thứ tự | Có thể thay đổi | Tìm theo key | Tìm theo index | Tình huống dùng |
|---|---|---|---|---|---|---|
| OrderedDictionary | Có "khóa-giá trị" | Có | Có | Có | Có | Bản đồ, config, báo cáo |
| ReadOnlySet | Không, chỉ giá trị | Không | Không | Có | Không | Set an toàn, hằng số |
| HashSet | Không, chỉ giá trị | Không | Có | Có | Không | Set cần sửa đổi |
| Dictionary | Có "khóa-giá trị" | Không | Có | Có | Không | Bản đồ không cần thứ tự |
GO TO FULL VERSION