0Pricing
C# Academy · บทเรียน

ความหมายของการประเมินค่าแบบขี้เกียจ

ทำความเข้าใจการทำงานของตัววนซ้ำที่เลื่อนออกไป

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

การประมวลผลแบบเลื่อนเวลา

ตัววนซ้ำและคำค้นหา LINQ ใช้ การประมวลผลแบบเลื่อนเวลา โดยการกำหนดคำค้นหาแทบไม่ทำงานใด ๆ งานจะเริ่มขึ้นเมื่อคุณแจกแจงผลลัพธ์จริง ๆ ทีละรายการ

ยังไม่มีอะไรทำงานจนกว่าจะเริ่มแจกแจง

การเรียกเมธอดตัววนซ้ำไม่ได้ทำให้เนื้อหาภายในเมธอดทำงาน เนื้อหาจะเริ่มทำงานเมื่อ foreach (หรือผู้ใช้ผลลัพธ์รายอื่น) ดึงค่าแรก

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Numbers()
    {
        Console.WriteLine("-- iterator body started --");
        yield return 1;
        yield return 2;
    }

    public static void Main()
    {
        var seq = Numbers();
        Console.WriteLine("query created, body not run yet");
        foreach (var n in seq) Console.WriteLine(n);
    }
}

สร้างค่าทีละรายการ

การวนซ้ำแต่ละครั้งจะทำให้ตัววนซ้ำทำงานเพียงเท่าที่จำเป็นเพื่อสร้างค่าถัดไป รูปแบบการทำงานที่ให้ผู้ใช้ดึงค่าเช่นนี้ช่วยลดการใช้หน่วยความจำ แม้จะเป็นลำดับข้อมูลขนาดใหญ่มาก

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Trace()
    {
        for (int i = 1; i <= 3; i++)
        {
            Console.WriteLine("producing " + i);
            yield return i;
        }
    }

    public static void Main()
    {
        foreach (var n in Trace())
            Console.WriteLine("consumed " + n);
    }
}

การแจกแจงซ้ำจะทำงานอีกครั้ง

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

using System;
using System.Collections.Generic;

public class Program
{
    static int _calls = 0;
    static IEnumerable<int> Counter()
    {
        _calls++;
        yield return _calls;
    }

    public static void Main()
    {
        var seq = Counter();
        foreach (var n in seq) Console.WriteLine(n);
        foreach (var n in seq) Console.WriteLine(n);
        Console.WriteLine("total runs: " + _calls);
    }
}

การอ่านสถานะปัจจุบัน

เนื่องจากการทำงานถูกเลื่อนเวลา ตัววนซ้ำจึงอ่าน source เมื่อมีการแจกแจง ไม่ใช่เมื่อกำหนด ดังนั้นการเปลี่ยนแปลงที่เกิดขึ้นหลังจากกำหนด query จะปรากฏให้เห็น

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1, 2 };
        IEnumerable<int> doubled = Double(list);
        list.Add(3); // added before enumeration
        foreach (var n in doubled) Console.WriteLine(n);
    }

    static IEnumerable<int> Double(IEnumerable<int> nums)
    {
        foreach (var n in nums) yield return n * 2;
    }
}

การบังคับให้ทำงานทันที

หากต้องการเก็บภาพรวมของผลลัพธ์ ให้ทำให้ลำดับข้อมูลเป็นข้อมูลจริงด้วย ToList() หรือ ToArray() การทำเช่นนี้จะเรียกใช้ตัววนซ้ำหนึ่งครั้งและจัดเก็บค่าไว้ ทำให้ไม่ขึ้นต่อกับการเปลี่ยนแปลงของ source ในภายหลัง

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

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1, 2 };
        List<int> snapshot = Double(list).ToList(); // executed now
        list.Add(99);
        Console.WriteLine(string.Join(",", snapshot));
    }

    static IEnumerable<int> Double(IEnumerable<int> nums)
    {
        foreach (var n in nums) yield return n * 2;
    }
}

การหยุดทำงานเมื่อได้คำตอบช่วยลดงาน

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

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

public class Program
{
    static IEnumerable<int> Numbers()
    {
        for (int i = 1; ; i++)
        {
            Console.WriteLine("checking " + i);
            yield return i;
        }
    }

    public static void Main()
    {
        int firstBig = Numbers().First(n => n > 3);
        Console.WriteLine("found " + firstBig);
    }
}

ข้อผิดพลาดที่เลื่อนเวลา

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

using System;
using System.Collections.Generic;

public class Program
{
    static IEnumerable<int> Risky(int divisor)
    {
        yield return 10 / divisor; // throws only during enumeration
    }

    public static void Main()
    {
        var seq = Risky(0);
        Console.WriteLine("no error yet");
        try { foreach (var n in seq) Console.WriteLine(n); }
        catch (DivideByZeroException) { Console.WriteLine("caught during enumeration"); }
    }
}

การผสานสายการประมวลผลแบบเลื่อนเวลาทำได้อย่างประหยัด

การเชื่อมตัวดำเนินการแบบเลื่อนเวลาหลายตัวไม่ได้ทำให้ตัวดำเนินการเหล่านั้นทำงานซ้ำ ๆ แต่ละค่าจะไหลผ่านสายการประมวลผลทั้งหมดเพียงครั้งเดียวเมื่อมีการร้องขอ จึงมีประสิทธิภาพและใช้หน่วยความจำอย่างเหมาะสม

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

public class Program
{
    public static void Main()
    {
        var result = Enumerable.Range(1, 1000000)
            .Where(n => n % 2 == 0)
            .Select(n => n * n)
            .Take(3);
        foreach (var n in result) Console.WriteLine(n);
    }
}

ควรทำให้ข้อมูลเป็นจริงเมื่อใด

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

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

public class Program
{
    public static void Main()
    {
        var evens = Enumerable.Range(1, 10).Where(n => n % 2 == 0).ToList();
        // Safe to enumerate many times now
        Console.WriteLine("count: " + evens.Count);
        Console.WriteLine("sum: " + evens.Sum());
    }
}

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

สังเกตการประมวลผลแบบเลื่อนเวลาโดยตรง: กำหนด query จากนั้นเปลี่ยนแปลง source และการเปลี่ยนแปลงจะปรากฏในผลลัพธ์เมื่อมีการแจกแจงเท่านั้น

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

public class Program
{
    public static void Main()
    {
        var source = new List<int> { 1, 2, 3 };
        var query = source.Where(n => n > 1); // not executed yet
        source.Add(10);
        source.Remove(2);
        // Executes now, reflecting all changes
        Console.WriteLine(string.Join(", ", query));
    }
}

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

ใช้เหตุผลเกี่ยวกับการประมวลผลแบบเลื่อนเวลา

สรุปทบทวน

การประเมินแบบเลื่อนเวลา (แบบขี้เกียจ) จะทำงานกับลำดับข้อมูลเมื่อมีการแจกแจงเท่านั้น

  • การกำหนด query ไม่ทำงานใด ๆ แต่การใช้ผลลัพธ์จะทำให้เกิดการทำงาน
  • การแจกแจงซ้ำจะเรียกใช้ตัววนซ้ำอีกครั้ง
  • ระบบจะอ่าน source เมื่อมีการแจกแจง ดังนั้นการเปลี่ยนแปลงภายหลังจึงปรากฏให้เห็น
  • ข้อผิดพลาดจะปรากฏระหว่างการแจกแจง
  • ใช้ ToList()/ToArray() เพื่อทำให้ข้อมูลเป็นจริงเมื่อจำเป็น

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

บทเรียน “ความหมายของการประเมินค่าแบบขี้เกียจ” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “ความหมายของการประเมินค่าแบบขี้เกียจ”

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

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

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

บทเรียน “ความหมายของการประเมินค่าแบบขี้เกียจ” ใช้เวลานานแค่ไหน

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

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

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

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

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