Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

วิธีใช้ Take และ Skip Operator ร่วมกันใน LINQ C#

Take และ Skip Operators ใน LINQ

Take(n) - คืน n Element แรกจาก Sequence
Skip(n) - ข้าม n Element แรก คืน Element ที่เหลือ

ร่วมกันใช้สำหรับ Pagination (แบ่งหน้า) และ Windowing

Syntax


// Method Syntax
var result = source.Skip(skipCount).Take(takeCount);

// Query Syntax
var result = (from item in source select item)
             .Skip(skipCount)
             .Take(takeCount);

ตัวอย่างที่ 1: Skip แยกต่างหาก

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List numbers = new List 
        { 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 5, 6, 7, 7, 8, 8 };
        
        Console.WriteLine($"Total Count: {numbers.Count}");  // 28
        
        // Skip 5 ตัวแรก
        var skipResult = numbers.Skip(5);
        Console.WriteLine($"Skip(5) Count: {skipResult.Count()}");  // 23
        Console.WriteLine($"Skip(5) Values: {string.Join(", ", skipResult.Take(10))}...");  // 6,7,8,9,11,1,2,3,4,5
    }
}

ผลลัพธ์:

Total Count: 28
Skip(5) Count: 23

ตัวอย่างที่ 2: Take แยกต่างหาก


var takeResult = numbers.Take(5);
Console.WriteLine($"Take(5) Count: {takeResult.Count()}");  // 5
Console.WriteLine($"Take(5) Values: {string.Join(", ", takeResult)}");  // 1,2,3,4,5

ผลลัพธ์:

Take(5) Count: 5

ตัวอย่างที่ 3: Skip และ Take ร่วมกัน (Pagination)


// ข้าม 10 ตัว แล้วเอา 18 ตัว
var skipTakeResult = numbers.Skip(10).Take(18);
Console.WriteLine($"Skip(10).Take(18) Count: {skipTakeResult.Count()}");  // 18
Console.WriteLine($"Values: {string.Join(", ", skipTakeResult)}");

ผลลัพธ์:

Skip(10).Take(18) Count: 18
Values: 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 5, 6, 7, 7, 8, 8

Paging Helper Method


public static class LinqExtensions
{
    public static IEnumerable Page(this IEnumerable source, int pageNumber, int pageSize)
    {
        if (pageNumber < 1) pageNumber = 1;
        if (pageSize < 1) pageSize = 10;
        
        return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
    }
    
    // สำหรับ IQueryable (Database) - Return IQueryable
    public static IQueryable Page(this IQueryable source, int pageNumber, int pageSize)
    {
        if (pageNumber < 1) pageNumber = 1;
        if (pageSize < 1) pageSize = 10;
        
        return source.Skip((pageNumber - 1) * pageSize).Take(pageSize);
    }
}

// Usage
var page1 = numbers.Page(1, 5);   // 1,2,3,4,5
var page2 = numbers.Page(2, 5);   // 6,7,8,9,11
var page3 = numbers.Page(3, 5);   // 1,2,3,4,5

TakeLast / SkipLast (.NET 6+)


var numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// TakeLast: เอา 3 ตัวท้าย
var last3 = numbers.TakeLast(3);  // 8, 9, 10

// SkipLast: ข้าม 3 ตัวท้าย
var exceptLast3 = numbers.SkipLast(3);  // 1, 2, 3, 4, 5, 6, 7

Chunk (.NET 6+) - แบ่งเป็นกลุ่ม


// แบ่งเป็นกลุ่มละ 3
var chunks = numbers.Chunk(3);
// 結果: [[1,2,3], [4,5,6], [7,8,9], [10]]

foreach (var chunk in chunks)
{
    Console.WriteLine($"[{string.Join(", ", chunk)}]");
}

SQL Translation (EF Core)

LINQSQL ServerPostgreSQL/MySQL
.Skip(10).Take(5)OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLYLIMIT 5 OFFSET 10
.Take(5)TOP (5)LIMIT 5

Performance Tips

  • List/Array: Skip/Take ใช้ Indexer O(1) - เร็วมาก
  • IEnumerable (Iterator): Skip ต้อง Iterate ทุกตัว O(n)
  • Database: OFFSET มาก (Deep Paging) ช้า → ใช้ Keyset Paging
  • OrderBy จำเป็น: ก่อน Skip/Take ต้อง OrderBy เพื่อผลลัพธ์ Deterministic

Keyset Paging (Cursor-based) - แทน Skip สำหรับ Deep Paging


// แทน Skip(10000).Take(20)
var lastId = 10000;  // ID สุดท้ายของหน้าก่อน
var nextPage = await context.Products
    .Where(p => p.Id > lastId)
    .OrderBy(p => p.Id)
    .Take(20)
    .ToListAsync();

สรุป

  • Skip(n) = ข้าม n ตัวแรก
  • Take(n) = เอา n ตัวแรก
  • Skip(a).Take(b) = หน้าที่ (a/b)+1 ขนาด b
  • ต้อง OrderBy ก่อน เพื่อผลลัพธ์คงที่
  • .NET 6+: TakeLast, SkipLast, Chunk
  • Deep Paging ใช้ Keyset Paging (WHERE Id > @lastId)