ความแตกต่างหลัก
| Operator | Select | SelectMany |
| Category | Projection | Projection (Flattening) |
| Input → Output | 1 Element → 1 Result | 1 Element → IEnumerable → Flatten to 1 Sequence |
| Result Type | IEnumerable<TResult> | IEnumerable<TResult> (Flattened) |
| ใช้เมื่อไหร่ | Mapping แบบ 1-1 | Flatten Nested Collections |
ตัวอย่างเปรียบเทียบ
class Demo
{
public string Name { get; set; }
public List<string> Contents { get; set; }
public static List<Demo> GetAllContents()
{
return new List<Demo>
{
new Demo { Name = "Cap", Contents = new List<string> { "Nike", "Adidas" } },
new Demo { Name = "Shoes", Contents = new List<string> { "Nike", "Puma", "Adidas" } },
};
}
}
class Program
{
static void Main()
{
// SELECT: คืน IEnumerable> - ยังคงโครงสร้าง Nested
IEnumerable<List<string>> resultSelect = Demo.GetAllContents().Select(s => s.Contents);
Console.WriteLine("=== Select (Nested) ===");
foreach (List<string> list in resultSelect)
{
foreach (string s in list)
Console.WriteLine(s);
}
// SELECTMANY: Flatten เป็น IEnumerable เดียว
IEnumerable<string> resultSelectMany = Demo.GetAllContents().SelectMany(s => s.Contents);
Console.WriteLine("\n=== SelectMany (Flattened) ===");
foreach (string s in resultSelectMany)
{
Console.WriteLine(s);
}
Console.ReadKey();
}
}
Output
=== Select (Nested) ===
Nike
Adidas
Nike
Puma
Adidas
=== SelectMany (Flattened) ===
Nike
Adidas
Nike
Puma
Adidas
SelectMany Overloads
// 1. Basic: TSource → IEnumerable
source.SelectMany(x => x.Children)
// 2. With Index: TSource, int → IEnumerable
source.SelectMany((x, i) => x.Children)
// 3. Result Selector: Flatten พร้อม Map Result
source.SelectMany(
x => x.Orders, // Collection Selector
(x, order) => new { x.CustomerName, order.OrderId, order.Amount } // Result Selector
)
// 4. Full: Collection Selector + Result Selector + Index
source.SelectMany(
(x, i) => x.Orders,
(x, order, i) => new { x.CustomerName, order.OrderId, Index = i }
)
Use Cases จริง
1. Flatten List of Lists
List<List<int>> matrix = new() { new(){1,2}, new(){3,4}, new(){5} };
var flat = matrix.SelectMany(x => x); // 1,2,3,4,5
2. Query Child Collections (Master-Detail)
// Orders ของ Customer ทุกคน
var allOrders = customers.SelectMany(c => c.Orders);
// พร้อมข้อมูล Customer
var orderDetails = customers.SelectMany(
c => c.Orders,
(c, o) => new { c.Name, o.Id, o.Date, o.Total }
);
3. Cross Join (Cartesian Product)
var colors = new[] { "Red", "Blue" };
var sizes = new[] { "S", "M", "L" };
var combinations = colors.SelectMany(c => sizes.Select(s => $"{c}-{s}"));
// Red-S, Red-M, Red-L, Blue-S, Blue-M, Blue-L
Query Syntax
// Select
var q1 = from d in Demo.GetAllContents()
select d.Contents;
// SelectMany (from ... from ...)
var q2 = from d in Demo.GetAllContents()
from c in d.Contents
select c;