C# Academy · บทเรียน

ชนิด Enumerable แบบกำหนดเอง

นำ IEnumerable ไปใช้ด้วยตัววนซ้ำ

บทเรียน 4 จาก 413 ขั้นตอน

ชนิด Enumerable แบบกำหนดเอง เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

การสร้าง Enumerable ของคุณเอง

การนำ IEnumerable<T> ไปใช้กับคลาสทำให้คลาสนั้นใช้งานใน foreach ได้ เมื่อผสานกับเมธอดตัววนซ้ำ วิธีนี้ช่วยให้คุณสร้างคอลเลกชันและช่วงข้อมูลแบบกำหนดเองได้ด้วยโค้ดเพียงเล็กน้อย

การนำ GetEnumerator ไปใช้ด้วย yield

วิธีที่ง่ายที่สุดในการนำ IEnumerable<T> ไปใช้ คือเขียน GetEnumerator เป็นตัววนซ้ำโดยใช้ yield return คอมไพเลอร์จะสร้างตัวแจกแจงให้คุณ

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

public class IntRange : IEnumerable<int>
{
    private readonly int _start, _count;
    public IntRange(int start, int count) { _start = start; _count = count; }

    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _count; i++)
            yield return _start + i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new IntRange(5, 4))
            Console.WriteLine(n);
    }
}

เหตุใดจึงมีเมธอด GetEnumerator สองเมธอด

IEnumerable<T> สืบทอด IEnumerable ที่ไม่เป็นแบบทั่วไป ดังนั้นคุณจึงต้องนำไปใช้ทั้งสองแบบ ส่วนแบบไม่เป็นแบบทั่วไปที่ระบุอย่างชัดเจนจะส่งต่อการทำงานไปยังรุ่นแบบทั่วไป

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

public class Letters : IEnumerable<char>
{
    public IEnumerator<char> GetEnumerator()
    {
        yield return 'x';
        yield return 'y';
        yield return 'z';
    }
    // Required because IEnumerable<T> extends IEnumerable
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var c in new Letters())
            Console.WriteLine(c);
    }
}

การซ่อนตรรกะที่กำหนดเอง

Enumerable แบบกำหนดเองสามารถซ่อนกฎการสร้างที่ซับซ้อนไว้เบื้องหลังชนิดข้อมูลที่ใช้งานง่าย ในที่นี้คลาสจะส่งคืนลำดับ Fibonacci ตามความยาวที่เลือก

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

public class Fibonacci : IEnumerable<int>
{
    private readonly int _count;
    public Fibonacci(int count) { _count = count; }

    public IEnumerator<int> GetEnumerator()
    {
        int a = 0, b = 1;
        for (int i = 0; i < _count; i++)
        {
            yield return a;
            int next = a + b; a = b; b = next;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Fibonacci(8))
            Console.WriteLine(n);
    }
}

การห่อหุ้มคอลเลกชันภายใน

Enumerable แบบกำหนดเองมักห่อหุ้มที่จัดเก็บข้อมูลภายใน โดยเปิดให้วนซ้ำได้พร้อมควบคุมวิธีเพิ่มหรือกรองรายการ

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

public class EvenBag : IEnumerable<int>
{
    private readonly List<int> _items = new List<int>();
    public void Add(int n) { if (n % 2 == 0) _items.Add(n); }

    public IEnumerator<int> GetEnumerator() => _items.GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var bag = new EvenBag();
        bag.Add(1); bag.Add(2); bag.Add(3); bag.Add(4);
        foreach (var n in bag) Console.WriteLine(n);
    }
}

Enumerable แบบกำหนดเองทำงานร่วมกับ LINQ ได้

ชนิดข้อมูลใดก็ตามที่นำ IEnumerable<T> ไปใช้ จะได้รับความสามารถทั้งหมดของ LINQ ทันที คุณสามารถกรอง แปลงรูป และรวบรวมคอลเลกชันแบบกำหนดเองได้โดยไม่ต้องเขียนความสามารถเหล่านี้เพิ่ม

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

public class IntRange : IEnumerable<int>
{
    private readonly int _start, _count;
    public IntRange(int start, int count) { _start = start; _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 0; i < _count; i++) yield return _start + i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var range = new IntRange(1, 10);
        Console.WriteLine(range.Where(n => n % 3 == 0).Sum());
    }
}

แต่ละ foreach จะได้ตัวแจกแจงใหม่

เนื่องจากมีการเรียก GetEnumerator ทุกครั้งที่คุณวนซ้ำ Enumerable แบบกำหนดเองจึงสามารถแจกแจงซ้ำได้ โดยแต่ละรอบจะเริ่มต้นใหม่

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

public class IntRange : IEnumerable<int>
{
    private readonly int _count;
    public IntRange(int count) { _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 1; i <= _count; i++) yield return i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var r = new IntRange(3);
        foreach (var n in r) Console.Write(n);
        Console.WriteLine();
        foreach (var n in r) Console.Write(n); // fresh again
        Console.WriteLine();
    }
}

Enumerable แบบกำหนดเองชนิดทั่วไป

ทำให้ Enumerable เป็นชนิดทั่วไปเพื่อให้รองรับชนิดองค์ประกอบใดก็ได้ ในที่นี้ ring แบบเรียบง่ายจะแสดงรายการตามลำดับ โดยเริ่มจากตำแหน่งชดเชยที่เลือก

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

public class Ring<T> : IEnumerable<T>
{
    private readonly T[] _items;
    private readonly int _start;
    public Ring(T[] items, int start) { _items = items; _start = start; }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < _items.Length; i++)
            yield return _items[(_start + i) % _items.Length];
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var ring = new Ring<string>(new[] { "a", "b", "c", "d" }, 2);
        foreach (var s in ring) Console.WriteLine(s);
    }
}

Enumerable แบบกำหนดเองที่ทำงานแบบเลื่อนเวลา

เนื่องจาก GetEnumerator เป็นตัววนซ้ำในตัวเอง Enumerable แบบกำหนดเองของคุณจึงทำงานแบบเลื่อนเวลาโดยค่าเริ่มต้น รายการจะถูกสร้างขึ้นเมื่อผู้ใช้ผลลัพธ์ดึงค่าเท่านั้น

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

public class Squares : IEnumerable<int>
{
    private readonly int _count;
    public Squares(int count) { _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = 1; i <= _count; i++)
        {
            Console.WriteLine("computing " + i);
            yield return i * i;
        }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Squares(3))
            Console.WriteLine("got " + n);
    }
}

ควรสร้าง Enumerable แบบกำหนดเองเมื่อใด

เลือกใช้ Enumerable แบบกำหนดเองเมื่อการเรียงลำดับการวนซ้ำหรือตรรกะการสร้างเป็นส่วนสำคัญของชนิดข้อมูล เช่น การท่องผ่านต้นไม้ source ข้อมูลแบบแบ่งหน้า หรือลำดับข้อมูลทางคณิตศาสตร์ มิฉะนั้นเมธอดตัววนซ้ำทั่วไปก็เพียงพอ

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

public class Countdown : IEnumerable<int>
{
    private readonly int _from;
    public Countdown(int from) { _from = from; }
    public IEnumerator<int> GetEnumerator()
    {
        for (int i = _from; i >= 0; i--) yield return i;
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        foreach (var n in new Countdown(5)) Console.WriteLine(n);
    }
}

ลองทำด้วยตนเอง

สร้าง Enumerable แบบกำหนดเองที่สร้างลำดับข้อมูลเรขาคณิต จากนั้นใช้ LINQ กับลำดับนั้น ชนิดข้อมูลของคุณจะเชื่อมต่อกับระบบทั้งหมดได้โดยตรง

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

public class Geometric : IEnumerable<int>
{
    private readonly int _start, _ratio, _count;
    public Geometric(int start, int ratio, int count) { _start = start; _ratio = ratio; _count = count; }
    public IEnumerator<int> GetEnumerator()
    {
        int value = _start;
        for (int i = 0; i < _count; i++) { yield return value; value *= _ratio; }
    }
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Program
{
    public static void Main()
    {
        var seq = new Geometric(1, 2, 6);
        Console.WriteLine(string.Join(", ", seq));
        Console.WriteLine("sum: " + seq.Sum());
    }
}

ตรวจสอบอย่างรวดเร็ว

ทบทวนข้อกำหนดของ IEnumerable

สรุปทบทวน

Enumerable แบบกำหนดเองนำ IEnumerable<T> ไปใช้ จึงทำงานร่วมกับ foreach และ LINQ ได้

  • นำ GetEnumerator ไปใช้เป็นตัววนซ้ำด้วย yield return
  • จัดเตรียม GetEnumerator แบบไม่เป็นแบบทั่วไปที่ระบุอย่างชัดเจนด้วย
  • การวนซ้ำแต่ละครั้งจะได้ตัวแจกแจงใหม่
  • การวนซ้ำจะทำงานแบบเลื่อนเวลาโดยค่าเริ่มต้น
เริ่มต้นได้ฟรี

เรียนรู้ C# ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
93
บทเรียน
346

คำถามที่พบบ่อย

บทเรียน “ชนิด Enumerable แบบกำหนดเอง” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ชนิด Enumerable แบบกำหนดเอง” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ชนิด Enumerable แบบกำหนดเอง”

นำ IEnumerable ไปใช้ด้วยตัววนซ้ำ คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ชนิด Enumerable แบบกำหนดเอง” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม

ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เมธอดตัววนซ้ำด้วย yield return
  2. yield break และการสิ้นสุดก่อนกำหนด
  3. ความหมายของการประเมินค่าแบบขี้เกียจ
  4. ชนิด Enumerable แบบกำหนดเอง
← กลับไปที่ C# Academy