CodeGym /Các khóa học /C# SELF /Đặt tên cho các phần tử và giải cấu trúc tuple

Đặt tên cho các phần tử và giải cấu trúc tuple

C# SELF
Mức độ, Bài học
Có sẵn

1. Đặt tên tuple: bối cảnh cho giải cấu trúc

Ở bài trước tụi mình đã làm quen với tuple có phần tử đặt tên, giúp code dễ đọc hơn nhiều, thay vì phải truy cập giá trị qua mấy cái tên vô hồn như Item1, Item2, mà dùng tên có ý nghĩa (ví dụ person.Name). Việc đặt tên cho phần tử tuple đặc biệt quan trọng khi dùng tuple làm giá trị trả về từ method, parameter hoặc property. Chính việc đặt tên này là tiền đề quan trọng cho việc giải cấu trúc tiện lợi mà tụi mình sẽ học tiếp trong bài này.

Nhắc lại nè, bạn có thể đặt tên khi khởi tạo tuple bằng literal, hoặc trong signature của return value của method, property hay field.

Ví dụ với method: Tuple có phần tử đặt tên trong return value của method


public static (int Tuoi, string Ten) GetPetInfo()
{
    return (Tuoi: 5, Ten: "Barsik");
}

// Sử dụng:
var info = GetPetInfo();
Console.WriteLine($"{info.Ten} — {info.Tuoi} tuổi");

Ví dụ với field/property: Tuple có phần tử đặt tên trong field/property


public (int Rong, int Cao) KichThuocAnh = (1024, 768);

Nhớ nhé: nếu không đặt tên rõ ràng, các phần tử vẫn sẽ truy cập được qua Item1, Item2 v.v. Điều này làm code khó đọc hơn, nhất là khi dùng tuple nhiều lần. Vì vậy, luôn nên đặt tên có ý nghĩa cho phần tử, trừ khi ý nghĩa đã quá rõ ràng từ context rồi.

2. Giải cấu trúc tuple

Giải cấu trúc là gì?

Giải cấu trúc là quá trình "bóc tách" tuple thành các biến riêng biệt để dễ thao tác. Tức là, từ tuple (Tuoi: 5, Ten: "Barsik") bạn lấy ra hai biến: tuoi và ten.

Ví dụ dễ hiểu: Hãy tưởng tượng tuple là cái hộp có các ngăn được dán nhãn. Giải cấu trúc là bạn lấy từng món trong hộp ra để đúng chỗ trên bàn luôn.

Cú pháp giải cấu trúc


var pet = (Tuoi: 5, Ten: "Barsik");
var (tuoi, ten) = pet;
Console.WriteLine($"{ten} — {tuoi} tuổi");
Giải cấu trúc tuple thành biến

Giờ bạn có hai biến: tuoi và ten. Chúng nhận giá trị từ tuple. Tên bên trái (tuoi, ten) không nhất thiết phải giống tên trong tuple đâu, chỉ là biến local mới thôi.

Giải cấu trúc return value của function

Tuple thường dùng để trả về nhiều giá trị từ function. Lúc này giải cấu trúc cực kỳ tiện:


public static (double nhoNhat, double lonNhat) GetMinMax(int[] duLieu)
{
    int nhoNhat = duLieu.Min();
    int lonNhat = duLieu.Max();
    return (nhoNhat, lonNhat);
}

var so = new[] { 1, 2, 3, 4, 5 };
var (giaTriNhoNhat, giaTriLonNhat) = GetMinMax(so);
Console.WriteLine($"Nhỏ nhất: {giaTriNhoNhat}, lớn nhất: {giaTriLonNhat}");
Giải cấu trúc return value của function

Lưu ý là khi giải cấu trúc, bạn đặt tên biến sao cho tiện với context hiện tại là được.

Giải cấu trúc với var

var (a, b) = (10, 20); // int a = 10, b = 20

Giải cấu trúc và discard _

Đôi khi bạn không cần tất cả phần tử trong tuple. Có thể bỏ qua bằng _ (discard). Discard (_) trong tuple là cách nói "biết là có phần tử này nhưng không cần, khỏi tạo biến cho nó".

Tức là bạn giải cấu trúc tuple nhưng để lại "lỗ trống" ở chỗ không cần giá trị. Ngắn gọn, tiện lợi và không rườm rà!


var pet = (Tuoi: 5, Ten: "Barsik", VuiVe: true);
var (tuoi, _, vuiVe) = pet; // chỉ lấy tuoi và vuiVe, bỏ qua ten
Giải cấu trúc với discard _

Một điều thú vị: discard dùng nhiều để không làm bẩn namespace với mấy biến không cần thiết.

Giải cấu trúc trong vòng lặp foreach

Trong C# bạn có thể duyệt mảng tuple và giải cấu trúc ngay trong foreach luôn:


var pets = new (string Ten, int Tuoi)[]
{
    ("Barsik", 5),
    ("Musya", 3),
    ("Johnny", 7)
};

foreach (var (ten, tuoi) in pets)
{
    Console.WriteLine($"{ten} — {tuoi} tuổi");
}
Giải cấu trúc tuple trong foreach

3. Cách hoạt động của tên phần tử và kiểu dữ liệu

Hành vi của phần tử đặt tên

Tên phần tử tuple chỉ là "đường mật cú pháp" thôi, tức là để dễ cho người đọc. Khi compile, tên sẽ thành field nội bộ Item1, Item2 v.v., nhưng IDE và compiler vẫn giữ tên để bạn dùng cho tiện.

Ảnh hưởng đến compatibility kiểu và ép kiểu

Hai tuple có cùng số phần tử và kiểu, nhưng tên khác nhau, vẫn được coi là cùng kiểu với compiler. Tên phần tử không ảnh hưởng đến định nghĩa kiểu:


var t1 = (X: 42, Y: 13);
var t2 = (A: 42, B: 13);
t1 = t2; // OK
Console.WriteLine(t1.X); // 42
Compatibility của tuple với tên phần tử khác nhau

Tuy nhiên khi làm việc với expression và gợi ý IntelliSense thì tên bên trái (nơi gán) sẽ được dùng, không phải tên bên phải.

Chỉ định tên rõ ràng và không rõ ràng

Đã học rồi, nhưng nhắc lại là bạn có thể tạo tuple với một phần tử đặt tên, phần còn lại không, hoặc không đặt tên gì hết — lúc đó sẽ là Item1 v.v.


var diem = (X: 10, 20); // X và Item2
Console.WriteLine(diem.X);     // 10
Console.WriteLine(diem.Item2); // 20
Tuple đặt tên một phần tử

Tip: luôn đặt tên phần tử nếu tuple có hơn một-hai phần tử, hoặc nếu ý nghĩa giá trị không rõ ràng từ context.

4. Lỗi phổ biến và lưu ý khi làm việc với tên và giải cấu trúc

Lỗi #1: “chuyển nhà” tên khi gán tuple khác nhau
Nếu bạn khai báo tuple với tên này, rồi gán cho nó tuple khác không có tên (hoặc tên khác), thì trong IntelliSense vẫn hiện tên ban đầu. Ví dụ:


var goc = (X: 1, Y: 2);
var bietDanh = goc;            // bietDanh.X == 1, bietDanh.Y == 2

goc = (10, 20);                // tuple không tên
Console.WriteLine(bietDanh.X); // vẫn chạy, bietDanh vẫn giữ tên cũ

Lỗi #2: trùng tên phần tử.
Không được đặt hai phần tử cùng tên — compiler sẽ báo lỗi “Duplicate tuple element name”:


var tupleLoi = (A: 1, A: 2);     // Lỗi CS8122: Duplicate tuple element name 'A'

Lỗi #3: giải cấu trúc sai số lượng phần tử.
Khi giải cấu trúc, số biến phải đúng bằng số phần tử tuple. Sai là lỗi liền:


var pet = (Tuoi: 5, Ten: "Barsik");
var (tuoi, ten, tamTrang) = pet;     // Lỗi CS8124: Tuple must contain exactly 3 elements

Lỗi #4: dùng discard _ sai cách
Bỏ qua phần tử bằng _ chỉ áp dụng cho từng vị trí riêng biệt, không “gộp” các chỗ bỏ qua thành một. Nếu bạn cố bỏ qua hai phần tử bằng một _, sẽ bị lỗi:


var duLieu = (1, 2, 3);
var (_, x, _) = duLieu;            // Đúng: bỏ qua phần tử 1 và 3
var (_, _) = duLieu;               // Lỗi CS8124: Tuple must contain exactly 2 elements

2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Tạo tuple có tên và in ra các phần tử
Tạo tuple có tên và in ra các phần tử
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Giải nén giá trị trả về của phương thức
Giải nén giá trị trả về của phương thức
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION