.NET 4 – System.Tuple vs Anonymous Type

Net 4.0 cho ra mắt một nhóm class mới với cùng tên là Tuple. Mục đích của các class là tạo một đối tượng lưu trữ cho các dữ liệu phức tạp, và nhờ đó người dùng khỏi cần phải tạo thêm một class mới. Nhưng từ .Net 3, bạn đã có thể sử dụng anonymous type để làm điều này với cú pháp đơn giản hơn. Vậy thì System.Tuple phải chăng là dư thừa?

Tuple là gì?

.Net 4 cung cấp cho người dùng 8 class generic Tuple<> và một static class Tuple để tạo ra các thể hiện của mỗi class Tuple<> tương ứng với phương thức Tuple.Create(). Mỗi class Tuple<> đã được định nghĩa sẵn các property có tên là Item1, Item2, Item3,… dựa vào số lượng tham số của constructor.
Lưu ý rằng các property phải được gán giá trị thông qua constructor vì nó chỉ cho phép bạn lấy giá trị (read only).
public class Tuple
public class Tuple <T1, T2>
public class Tuple <T1, T2, T3>
public class Tuple <T1, T2, T3, T4>
public class Tuple <T1, T2, T3, T4, T5>
public class Tuple <T1, T2, T3, T4, T5, T6>
public class Tuple <T1, T2, T3, T4, T5, T6, T7>
public class Tuple <T1, T2, T3, T4, T5, T6, T7, TRest>
3d tuple
Công dụng của class này là để bạn khỏi phải tạo các class để lưu trữ các kiểu dữ liệu phức tạp và tạm thời. Một giải pháp khác là dùng mảng object[] để lưu dữ liệu nhưng điều này ảnh hưởng tới hiệu suất thực thi do phải thực hiện chuyển đổi kiểu. Với Tuple, bạn có thể xác định bất kì kiểu tham số nào cần khởi tạo:

static void Main(string[] args)
{
var t1 = new Tuple<int, string, bool>(1234, “Hello World”,true);
Console.WriteLine(t1.Item1 * 2); // 246
Console.WriteLine(t1.Item2.ToUpper()); // HELLO WORLD
Console.WriteLine(t1.Item3?”One”:”Two”); // One
Console.ReadKey();
}

Một điểm nữa là các Tuple có thể so sánh với nhau bằng phương thức Equals() bằng cách dựa vào từng property của chúng. Việc so sánh không chỉ dựa vào dữ liệu của các property mà còn dựa vào thứ tự của chúng:
static void Main(string[] args)
{
var t1 = new Tuple<int, string, bool>(1234, “Hello World”, true);
var t2 = new Tuple<int, string, bool>(1234, “Hello World”, true);
var t3 = new Tuple<int, bool, string>(1234, true, “Hello World”);
Console.WriteLine(t1.Equals(t2)); // True
Console.WriteLine(t1.Equals(t3)); // False
Console.ReadKey();
}
Anonymous Type làm được không?

Hãy thử lại ví dụ đầu tiên với anonymous type, nó không chỉ linh hoạt hơn trong việc cho phép người dùng xác định số lượng tham số mà còn có thể đặt tên bất kì cho các tham số:
static void Main(string[] args)
{
var a1 = new { Item100 = 1234, Item200 = “Hello World”, Item300 = true };
Console.WriteLine(a1.Item100 * 2); // 246
Console.WriteLine(a1.Item200.ToUpper()); // HELLO WORLD
Console.WriteLine(a1.Item300 ? “One” : “Two”); // One
Console.ReadKey();
}
Anonymous có so sánh được không? Tất nhiên là được, nhưng nó vẫn phụ thuộc vào tên của property và thứ tự các tham số:
static void Main(string[] args)
{
var a1 = new { A = 1234, B = “Hello World”, C = true };
var a2 = new { A = 1234, B = “Hello World”, C = true };
var a3 = new { A = 1234, C = true, B = “Hello World”, };
Console.WriteLine(a1.Equals(a2)); // True
Console.WriteLine(a1.Equals(a3)); // False
Console.ReadKey();
}
“Vậy tôi có cần đến Tuple?”

Câu trả lời là: Tôi không chắc. Điều này thực sự có thể gây một số tranh cãi nhưng cá nhân tôi cho rằng có thể class này sẽ không cần thiết (được thay thế bởi một kĩ thuật khác).
Đối với bạn, có thể câu trả lời là:
Không: Chỉ cần sử dụng anonymous type là đủ.
Có: Sử dụng Tuple để tạo đối tượng sẽ an toàn hơn anonymous type. Lý do là bạn bị áp đặt sử dụng các tên property cố định. Mặc dù điều này làm cho thông tin của các đối tượng thiếu rõ ràng hơn so với các anonymouse object. Tuy nhiên chúng đảm bảo rằng các đối tượng sẽ được so sánh chính xác hơn.
Trong một ví dụ nho nhỏ, bạn có thể tạo một class MyObject như sau:
class MyObject : Tuple<int, string, bool>
{
public MyObject(int item1, string item2, bool item3) :
base(item1, item2, item3) { }
}
class Program
{
static void Main(string[] args)
{
var obj1 = new MyObject(1234, “Hello World”, true);
var obj2 = new MyObject(1234, “Hello World”, true);
Console.WriteLine(obj1.Equals(obj2)); // True
Console.ReadKey();
}
}
Điều này giúp giảm bớt việc phải khai báo các property và vẫn có so sánh được các đối tượng với nhau dựa vào phương thức Equals(). Tuy nhiên hạn chế của nó là không thể thay đổi được các giá trị của property như đã nói ở phần đầu.
Một vấn đề nữa là khi làm việc các class generic khác, bạn ko thể dùng được anonymous type. Ví dụ như một List, bạn có thể đặt Tuple<> vào (List<Tuple<>>) thay vì tạo một class mới.
Cuối cùng là …

Bài này tôi viết chỉ có mục đích giới thiệu về class Tuple và nhân tiện nhắc lại về anonymous type. Trong thực tế, ít ai biết về class này, và cho dù có biết thì có lẽ đa số họ cũng không quan tâm đến.
Đối với tôi thì chưa bao giờ sử dụng đến class này khi viết chương trình. Tuy nhiên có thể có những ứng dụng hữu ích của class này mà tôi không biết.