Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> C#

Khác biệt giữa Select và SelectMany trong LINQ C#

So sánh cốt lõi

Đặc điểmSelectSelectMany
Kết quả mỗi element1 kết quả (1:1 mapping)0..N kết quả (1:N mapping)
Kiểu trả vềIEnumerable<TResult>IEnumerable<TResult> (flattened)
Input selectorFunc<T, TResult>Func<T, IEnumerable<TResult>>
Hành viProjection (chiếu)Projection + Flatten (làm phẳng)

Ví dụ minh họa trực quan


class Category 
{
    public string Name { get; set; }
    public List Products { get; set; }
    
    public static List GetCategories() 
    {
        return new List 
        {
            new Category { Name = "Electronics", Products = new() { "Phone", "Laptop", "Tablet" } },
            new Category { Name = "Clothing", Products = new() { "Shirt", "Pants" } },
            new Category { Name = "Books", Products = new() { "C# Guide", "LINQ Book" } }
        };
    }
}

Select - Trả về IEnumerable<List<string>> (Collection of Collections)


var result = Category.GetCategories()
    .Select(c => c.Products); // Mỗi category → 1 List

// Kết quả: IEnumerable>
// [
//   ["Phone", "Laptop", "Tablet"],
//   ["Shirt", "Pants"],
//   ["C# Guide", "LINQ Book"]
// ]

foreach (var productList in result) 
{
    foreach (var product in productList) 
    {
        Console.WriteLine(product);
    }
}

SelectMany - Trả về IEnumerable<string> (Flattened)


var result = Category.GetCategories()
    .SelectMany(c => c.Products); // Mỗi category → nhiều product → gộp thành 1 list

// Kết quả: IEnumerable (phẳng)
// ["Phone", "Laptop", "Tablet", "Shirt", "Pants", "C# Guide", "LINQ Book"]

foreach (var product in result) 
{
    Console.WriteLine(product);
}

Overloads của SelectMany

1. Basic: Func<T, IEnumerable<TResult>>


var allProducts = categories.SelectMany(c => c.Products);

2. With Index: Func<T, int, IEnumerable<TResult>>


var withIndex = categories.SelectMany((c, index) => c.Products.Select(p => $"{index}: {p}"));

3. Result Selector (Projection cuối): Func<T, TCollection, TResult>


// Kết hợp category name với product
var result = categories.SelectMany(
    c => c.Products,                    // Collection selector
    (category, product) => new { Category = category.Name, Product = product } // Result selector
);

// Kết quả: IEnumerable { Category, Product }

Use Cases thực tế

1. Flatten nested collections


// Tất cả OrderDetails từ tất cả Orders
var allDetails = orders.SelectMany(o => o.OrderDetails);

2. Cross Join (Cartesian Product)


var sizes = new[] { "S", "M", "L" };
var colors = new[] { "Red", "Blue" };

var combinations = sizes.SelectMany(size => colors.Select(color => $"{size}-{color}"));
// S-Red, S-Blue, M-Red, M-Blue, L-Red, L-Blue

3. Hierarchical Data Traversal


class Employee 
{
    public string Name { get; set; }
    public List Subordinates { get; set; }
}

// Lấy tất cả nhân viên trong cây (flatten hierarchy)
IEnumerable GetAllEmployees(Employee root) 
{
    return new[] { root }.Concat(root.Subordinates.SelectMany(GetAllEmployees));
}

4. Query Syntax


// SelectMany trong query syntax = từ khóa "from" thứ 2 trở đi
var query = from category in categories
            from product in category.Products
            select new { category.Name, product };
// Tương đương SelectMany

Performance Notes

  • Cả hai đều deferred execution (lười).
  • SelectMany tránh tạo intermediate collections so với Select + Concat.
  • Với collection lớn: cân nhắc AsParallel().SelectMany() (PLINQ).

SelectMany vs Select + SelectMany


// Cách cũ (trước SelectMany)
var oldWay = categories.Select(c => c.Products).SelectMany(p => p);

// Cách hiện đại
var newWay = categories.SelectMany(c => c.Products);