Tuple คืออะไร?
โครงสร้างข้อมูลที่จัดกลุ่มหลายค่า (Multiple Values) ไว้ใน Object เดียว โดยไม่ต้องสร้าง Class/Struct
- Return หลายค่าจาก Method (แทน ref/out)
- Pass หลายค่าเข้ Method ผ่าน Parameter เดียว
- Nesting ได้ (Tuple ใน Tuple)
- C# 7+: ValueTuple (Struct) - Performance ดีกว่า Tuple Class เดิม
Syntax แบบใหม่ (C# 7+ ValueTuple)
// Declaration with Names (Recommended)
(string Name, int Age, bool IsActive) person = ("John", 30, true);
// Access by Name
Console.WriteLine(person.Name); // John
Console.WriteLine(person.Age); // 30
// Access by Index (Item1, Item2...) - Backward Compatible
Console.WriteLine(person.Item1); // John
Use Case 1: Return Multiple Values
// แบบเก่า: out parameters, custom class, Tuple<T1,T2>
// แบบใหม่: ValueTuple - Clean Syntax
public (int Sum, int Count, double Average) CalculateStats(int[] numbers)
{
if (numbers.Length == 0) return (0, 0, 0.0);
int sum = numbers.Sum();
int count = numbers.Length;
double avg = (double)sum / count;
return (sum, count, avg);
}
// เรียกใช้
var (sum, count, avg) = CalculateStats(new[] { 1, 2, 3, 4, 5 });
Console.WriteLine($"Sum: {sum}, Count: {count}, Avg: {avg}");
// Output: Sum: 15, Count: 5, Avg: 3
Use Case 2: Pass Multiple Values as Single Parameter
public void DisplayUser((string FirstName, string LastName, int Age) user)
{
Console.WriteLine($"{user.FirstName} {user.LastName}, Age: {user.Age}");
}
// เรียกใช้
DisplayUser(("John", "Doe", 30));
// หรือ
var user = (FirstName: "Jane", LastName: "Smith", Age: 25);
DisplayUser(user);
Use Case 3: Deconstruction (แยกค่าออกมา)
var person = (Name: "Alice", Age: 28, City: "Bangkok");
// Full Deconstruction
var (name, age, city) = person;
// Partial Deconstruction (Discard _)
var (name, _, city) = person; // Age ถูก Discard
// Existing Variables
string name; int age;
(name, age, _) = person;
Use Case 4: Dictionary/Collection Operations
// Dictionary Deconstruction (C# 7+)
var dict = new Dictionary<string, int> { { "A", 1 }, { "B", 2 } };
foreach (var (key, value) in dict)
{
Console.WriteLine($"{key} = {value}");
}
// LINQ with Tuples
var results = data
.GroupBy(x => x.Category)
.Select(g => (Category: g.Key, Count: g.Count(), Items: g.ToList()));
Tuple vs Anonymous Type vs Class/Record
| Feature | Tuple (ValueTuple) | Anonymous Type | Record (C# 9+) |
|---|---|---|---|
| Named Members | ✅ | ✅ | ✅ |
| Return from Method | ✅ | ❌ (object) | ✅ |
| Pass as Parameter | ✅ | ❌ (object) | ✅ |
| Equality (Value-based) | ✅ | ✅ | ✅ |
| Immutability | ❌ (Mutable Struct) | ✅ | ✅ (init) |
| Methods/Behavior | ❌ | ❌ | ✅ |
| Serialization | Limited | Limited | Full Support |
เมื่อไหร่ควรใช้ Tuple?
- ✅ Return 2-4 ค่าที่เกี่ยวข้องกันชั่วคราว (Internal/Private Methods)
- ✅ LINQ Projections, GroupBy Results
- ✅ Deconstruction Patterns
- ✅ Throwaway Data, Temporary Grouping
เมื่อไหร่ ไม่ควร ใช้?
- ❌ Public API / Library Boundary (ใช้ Record/Class)
- ❌ Complex Domain Objects (มี Behavior, Validation)
- ❌ Long-lived Data (Mutable Struct ปัญหา Copy Semantics)
- ❌ Serialization/DTO (ใช้ Record/Class + JSON Attributes)