“ ผลผลิตหยุดพัก” อะไร ทำใน C #?


494

ฉันเคยเห็นรูปแบบนี้ใน MSDN: yield breakแต่ฉันไม่รู้ว่ามันทำอะไร มีใครรู้บ้าง


26
ทว่าเอกสารของ MSDN ระบุไว้ แต่ไม่ได้อธิบาย นั่นเป็นวิธีที่ไม่ดีในการหยอกล้อผู้พัฒนาความรู้ที่หิวโหย
Phil

3
ผลตอบแทนจากอัตราผลตอบแทนที่ไม่จำเป็นต้องสำหรับรายชื่อผู้สนับสนุนที่เป็นคุณไม่จำเป็นต้องมีอะไรบางอย่างเช่นรหัสเพียงแค่ทำMyList.Add(...) yield return ...หากคุณต้องการแยกวงออกก่อนเวลาอันควรและคืนรายการสำรองเสมือนที่คุณใช้yield break;
The Muffin Man

คำตอบ:


529

มันระบุว่าตัววนซ้ำสิ้นสุดลงแล้ว คุณสามารถคิดว่าyield breakเป็นreturnคำสั่งที่ไม่ส่งคืนค่า

ตัวอย่างเช่นหากคุณกำหนดฟังก์ชั่นเป็นตัววนซ้ำเนื้อความของฟังก์ชันอาจมีลักษณะดังนี้:

for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

โปรดทราบว่าหลังจากวนรอบเสร็จสิ้นรอบทั้งหมดบรรทัดสุดท้ายจะถูกเรียกใช้งานและคุณจะเห็นข้อความในแอปคอนโซลของคุณ

หรือเช่นนี้กับyield break:

int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

ในกรณีนี้คำสั่งสุดท้ายจะไม่ดำเนินการเพราะเราออกจากฟังก์ชั่นก่อน


8
มันอาจจะเป็นเพียงแค่การbreakแทนyield breakในตัวอย่างของคุณไป? คอมไพเลอร์ไม่ได้บ่นเรื่องนั้น
orad

85
@ หรือเรียบง่ายbreakในกรณีนี้จะหยุดวง แต่มันจะไม่ยกเลิกการดำเนินการวิธีการดังนั้นบรรทัดสุดท้ายจะถูกดำเนินการและ "จะไม่เห็นฉันข้อความ" จะเห็นจริง
Damir Zekić

5
@Damir Zekićคุณช่วยเพิ่มคำตอบของคุณได้ไหมว่าทำไมคุณถึงชอบที่จะได้รับผลตอบแทนมากกว่าผลตอบแทนและความแตกต่างจากทั้งสองคืออะไร?
Bruno Costa

11
@ DamirZekićที่ส่งคืนค่า null และการแบ่งผลตอบแทนไม่เหมือนกัน ถ้าคุณคืนค่า null คุณสามารถNullReferenceExceptionรับตัวแจงนับของ IEnumerable ได้ในขณะที่การหยุดรับผลตอบแทนนั้นคุณไม่มี (มีอินสแตนซ์ที่ไม่มีองค์ประกอบ)
Bruno Costa

6
@BrunoCosta พยายามที่จะให้ผลตอบแทนปกติในวิธีเดียวกันทำให้เกิดข้อผิดพลาดของคอมไพเลอร์ การใช้yield return xคอมไพเลอร์เตือนคุณต้องการให้เมธอดนี้เป็นน้ำตาล syntactic เพื่อสร้างEnumeratorวัตถุ แจงนับนี้มีวิธีการและทรัพย์สินMoveNext() CurrentMoveNext () ดำเนินการวิธีการจนกว่าจะมีคำสั่งและเปลี่ยนค่าที่เป็นyield return Currentครั้งต่อไปที่เรียกว่า MoveNext การดำเนินการจะดำเนินต่อจากที่นั่น yield breakตั้งค่าปัจจุบันเป็นโมฆะส่งสัญญาณการสิ้นสุดของตัวแจงนับเพื่อที่foreach (var x in myEnum())จะสิ้นสุด
Wolfzoon

57

สิ้นสุดบล็อกตัววนซ้ำ (เช่นบอกว่าไม่มีองค์ประกอบอีกต่อไปใน IEnumerable)


2
ด้วย [+1] - แม้ว่าในเชิงวิชาการจะไม่มีตัววนซ้ำใน. NET ตัวแจงนับเท่านั้น (ทิศทางเดียวไปข้างหน้า)
Shaun Wilson

@Shaun Wilson แต่ด้วยyieldคำหลักคุณสามารถทำซ้ำคอลเลกชันได้ทั้งสองทิศทางยิ่งไปกว่านั้นคุณสามารถใช้องค์ประกอบทุกอย่างของคอลเลคชั่นต่อไปไม่ได้อยู่ในแถวเดียวกันได้
2560

@ เดือนที่คุณสามารถทำซ้ำคอลเลกชันในแฟชั่นใด ๆ และคุณไม่จำเป็นต้องyieldทำมัน ฉันไม่ได้บอกว่าคุณผิดฉันเห็นสิ่งที่คุณแนะนำ แต่ในเชิงวิชาการไม่มีตัววนซ้ำใน. NET มีเพียงตัวแจงนับ (ทิศทางเดียวไปข้างหน้า) - ซึ่งแตกต่างจาก stdc ++ ไม่มี "กรอบตัววนซ้ำทั่วไป" ที่กำหนดไว้ใน CTS / CLR LINQ ช่วยปิดช่องว่างด้วยวิธีการส่วนขยายที่ใช้yield return และวิธีการโทรกลับแต่เป็นวิธีส่วนขยายชั้นหนึ่งไม่ใช่ตัววนซ้ำชั้นหนึ่ง ผลลัพธ์IEnumerableไม่สามารถวนซ้ำไปในทิศทางอื่นนอกจากการส่งต่อผู้โทร
Shaun Wilson

29

บอกตัววนซ้ำว่าถึงจุดสิ้นสุด

ตัวอย่างเช่น:

public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}

21

yieldโดยทั่วไปจะทำให้IEnumerable<T>วิธีการทำงานในลักษณะคล้ายกันกับด้าย (กำหนดไว้ล่วงหน้า)

yield returnเป็นเหมือนเธรดที่เรียกใช้ฟังก์ชัน "schedule" หรือ "sleep" เพื่อยกเลิกการควบคุม CPU เช่นเดียวกับเธรดIEnumerable<T>เมธอดจะควบคุมอีกครั้งที่จุดทันทีหลังจากนั้นตัวแปรโลคัลทั้งหมดที่มีค่าเดียวกับที่เคยมีก่อนที่จะถูกควบคุม

yield break เป็นเหมือนเธรดที่ถึงจุดสิ้นสุดของฟังก์ชันและสิ้นสุด

ผู้คนพูดถึง "เครื่องรัฐ" แต่เครื่องรัฐเป็น "เกลียว" ทั้งหมดจริงๆ เธรดมีสถานะบางอย่าง (ค่า Ie ของตัวแปรโลคัล) และแต่ละครั้งที่มีการกำหนดเวลาไว้จะต้องดำเนินการบางอย่างเพื่อให้ได้สถานะใหม่ จุดสำคัญเกี่ยวกับการyieldที่ไม่เหมือนกับเธรดระบบปฏิบัติการที่เราคุ้นเคยรหัสที่ใช้จะถูกตรึงในเวลาจนกว่าการวนซ้ำนั้นจะเป็นขั้นสูงหรือสิ้นสุดด้วยตนเอง



9

yield breakคำสั่งที่ทำให้เกิดการแจงนับหยุด มีผลบังคับใช้yield breakทำการแจงนับโดยไม่ต้องส่งคืนรายการเพิ่มเติมใด ๆ

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

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

ในตัวอย่างข้างต้นวิธี iterator จะหยุดดำเนินการตามธรรมชาติเมื่อmaxCountพบช่วงเวลา

yield breakคำสั่งเป็นวิธีการที่ iterator เพื่อยุติแจงอีก มันเป็นวิธีที่จะแยกออกจากการแจงนับก่อน นี่เป็นวิธีเดียวกันกับข้างบน เวลานี้เมธอดมีข้อ จำกัด เกี่ยวกับระยะเวลาที่เมธอดสามารถดำเนินการได้

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

yield breakขอให้สังเกตการเรียกร้องให้ ผลก็คือออกจากการแจงนับก่อน

แจ้งให้ทราบล่วงหน้าเกินไปว่าทำงานแตกต่างกันมากกว่าเพียงแค่ธรรมดาyield break breakในตัวอย่างข้างต้นออกมาจากวิธีการโดยไม่ทำให้การเรียกร้องให้yield breakDebug.WriteLine(..)


7

ที่นี่http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/เป็นตัวอย่างที่ดีมาก:

สาธารณะแบบคงที่ IEnumerable <int> ช่วง (int min, int max)
{
   ในขณะที่ (จริง)
   {
      if (min> = max)
      {
         ทำลายผลผลิต;
      }
      ผลตอบแทนคืนขั้นต่ำ ++;
   }
}

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


5

การหยุดรับผลตอบแทนเป็นเพียงวิธีการบอกผลตอบแทนเป็นครั้งสุดท้ายและไม่คืนค่าใด ๆ

เช่น

// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }

4

หากสิ่งที่คุณหมายถึงโดย "สิ่งที่ทำให้ผลตอบแทนที่แท้จริงทำ" คือ "มันทำงานอย่างไร" - ดูบล็อกของ Raymond Chen สำหรับรายละเอียดhttps://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273

ตัววนซ้ำ C # สร้างโค้ดที่ซับซ้อนมาก


12
พวกมันซับซ้อนเพียงถ้าคุณสนใจรหัสที่พวกเขารวบรวม รหัสที่ใช้พวกมันค่อนข้างง่าย
Robert Rossney

@ Robert นั่นคือสิ่งที่ฉันหมายถึงดังนั้นฉันจึงได้อัปเดตคำตอบเพื่อรวมความคิดเห็นของคุณ
Tony Lee

-2

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

ในการอธิบายความหมายโดยใช้ตัวอย่าง:

    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

ค่าที่ส่งคืนเมื่อมีการทำซ้ำคือ: 0, 2, 5

สิ่งสำคัญคือให้สังเกตว่าตัวแปรตัวนับในตัวอย่างนี้เป็นตัวแปรท้องถิ่น หลังจากการวนซ้ำครั้งที่สองซึ่งส่งคืนค่า 2 การวนซ้ำครั้งที่สามเริ่มต้นจากที่เหลือก่อนหน้าขณะที่รักษาค่าก่อนหน้าของตัวแปรโลคอลที่ชื่อว่าตัวนับซึ่งเป็น 2


11
คุณไม่ได้อธิบายสิ่งที่yield breakไม่
เจซัลลิแวน

ฉันไม่คิดว่าyield returnจริง ๆ แล้วสนับสนุนการคืนค่าหลายค่า บางทีนั่นอาจไม่ใช่สิ่งที่คุณตั้งใจจริงๆ แต่นั่นเป็นวิธีที่ฉันอ่าน
แซม

Sam - เมธอด SampleNumbers ที่มีหลายคำสั่งการคืนค่าผลตอบแทนไม่ได้ทำงานจริงค่าของตัววนซ้ำจะถูกส่งคืนทันทีและการดำเนินการจะกลับมาทำงานต่อเมื่อมีการร้องขอค่าถัดไป ฉันได้เห็นผู้คนยุติวิธีการเช่นนี้ด้วย "การหยุดรับผลตอบแทน" แต่ไม่จำเป็น การกดจุดสิ้นสุดของวิธีนี้จะสิ้นสุดตัววนซ้ำ
Loren Paulsen

เหตุผลที่นี่เป็นตัวอย่างที่ไม่ดีสำหรับเพราะyield breakมันไม่มีตัวแจงนับระดับภาษาเช่นforeach- เมื่อใช้ตัวแจงนับyield breakจะให้ค่าจริง ตัวอย่างนี้ดูเหมือนว่าลูปที่ไม่ได้ควบคุม คุณแทบจะไม่เคยเห็นรหัสนี้ในโลกแห่งความเป็นจริง (เราทุกคนสามารถนึกถึงกรณีขอบบางอย่างแน่นอน) และยังไม่มี "ตัววนซ้ำ" ที่นี่ "iterator block" ไม่สามารถขยายเกินกว่าวิธีการเป็นข้อกำหนดของภาษา สิ่งที่ส่งคืนจริง ๆ คือ "นับได้" ดูเพิ่มเติมได้ที่: stackoverflow.com/questions/742497//
Shaun Wilson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.