การใช้ 'ผลตอบแทนคืน' อย่างเหมาะสม


903

ผลผลิตคำหลักเป็นหนึ่งในบรรดาคำหลักใน C # ที่ยังคงทำให้ประหลาดใจฉันและฉันไม่เคยได้รับความมั่นใจว่าฉันใช้มันอย่างถูกต้อง

ของโค้ดสองชิ้นต่อไปนี้ซึ่งเป็นที่ต้องการและทำไม

เวอร์ชัน 1:การใช้การคืนผลตอบแทน

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        foreach (Product product in products)
        {
            yield return product;
        }
    }
}

เวอร์ชัน 2:ส่งคืนรายการ

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList<Product>();
    }
}

38
yieldผูกติดอยู่กับIEnumerable<T>ชนิดของมัน มันอยู่ในการประเมินผลที่ขี้เกียจ
Somaid

นี่คือคำตอบที่ดีสำหรับคำถามที่คล้ายกัน stackoverflow.com/questions/15381708/…
Sanjeev Rai

1
นี่คือตัวอย่างการใช้งานที่ดี: stackoverflow.com/questions/3392612/…
ValGe

6
ฉันเห็นกรณีที่ดีสำหรับการใช้yield returnถ้ารหัสที่วนซ้ำผ่านผลลัพธ์ของการGetAllProducts()อนุญาตให้ผู้ใช้มีโอกาสยกเลิกการประมวลผลก่อนกำหนด
JMD

2
ฉันพบหัวข้อนี้มีประโยชน์จริงๆ: programmers.stackexchange.com/a/97350/148944
PiotrWolkowski

คำตอบ:


806

ฉันมักจะใช้ผลตอบแทนเมื่อฉันคำนวณรายการถัดไปในรายการ (หรือแม้แต่กลุ่มรายการถัดไป)

เมื่อใช้เวอร์ชัน 2 คุณต้องมีรายการที่สมบูรณ์ก่อนส่งคืน โดยการใช้ผลตอบแทนคุณจะต้องมีรายการต่อไปก่อนที่จะส่งคืน

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

อีกกรณีหนึ่งที่ให้ผลตอบแทนดีกว่าคือถ้า IEnumerable แสดงถึงชุดที่ไม่มีที่สิ้นสุด พิจารณารายการหมายเลขเฉพาะหรือรายการสุ่มตัวเลขที่ไม่มีที่สิ้นสุด คุณไม่สามารถคืนค่า IEnumerable แบบเต็มได้ในครั้งเดียวดังนั้นคุณจึงใช้ return-return เพื่อส่งคืนรายการแบบเพิ่มหน่วย

ในตัวอย่างเฉพาะของคุณคุณมีรายการผลิตภัณฑ์ทั้งหมดดังนั้นฉันจะใช้เวอร์ชัน 2


31
ฉันต้องการ nitpick ที่ในตัวอย่างของคุณในข้อ 3 ทำให้เกิดประโยชน์สองอย่าง 1) มันกระจายต้นทุนการคำนวณ (บางครั้งก็เป็นประโยชน์ แต่บางครั้งก็ไม่ใช่) 2) มันอาจหลีกเลี่ยงการคำนวณอย่างไร้จุดหมายในการใช้งานหลายกรณีอย่างเกียจคร้าน คุณล้มเหลวในการพูดถึงข้อเสียเปรียบที่อาจเกิดขึ้นซึ่งจะช่วยให้อยู่ในระดับกลาง หากคุณมีสถานะระดับกลางจำนวนมาก (เช่น HashSet สำหรับการกำจัดซ้ำ) การใช้อัตราผลตอบแทนอาจทำให้เกิดรอยเท้าหน่วยความจำของคุณเพิ่มขึ้น
Kennet Belenky

8
นอกจากนี้หากองค์ประกอบของแต่ละบุคคลมีขนาดใหญ่มาก แต่พวกเขาจำเป็นต้องเข้าถึงตามลำดับเท่านั้นผลผลิตที่ได้จะดีกว่า
Kennet Belenky

2
และในที่สุด ... มีเทคนิคเล็กน้อยที่แปลกใหม่ แต่มีประสิทธิภาพในบางครั้งสำหรับการใช้ผลผลิตเพื่อเขียนโค้ดอะซิงโครนัสในรูปแบบที่ต่อเนื่องกันมาก
Kennet Belenky

12
อีกตัวอย่างที่น่าสนใจคือเมื่ออ่านไฟล์ CSV ที่ค่อนข้างใหญ่ คุณต้องการอ่านแต่ละองค์ประกอบ แต่คุณต้องการแยกการพึ่งพาออกไป ผลตอบแทนที่ได้คืนมาคือ IEnumerable <> จะช่วยให้คุณสามารถคืนค่าแต่ละแถวและประมวลผลแต่ละแถวได้ ไม่จำเป็นต้องอ่านไฟล์ 10 Mb ในหน่วยความจำ เพียงครั้งละหนึ่งบรรทัด
Maxime Rouiller

1
Yield returnดูเหมือนว่าเป็นการจดชวเลขสำหรับการเขียนคลาสตัววนซ้ำแบบกำหนดเองของคุณเอง (ใช้ IEnumerator) ดังนั้นประโยชน์ดังกล่าวยังใช้กับคลาสตัววนซ้ำแบบกำหนดเอง อย่างไรก็ตามโครงสร้างทั้งสองรักษาสถานะระดับกลาง ในรูปแบบที่ง่ายที่สุดมันเกี่ยวกับการเก็บการอ้างอิงไปยังวัตถุปัจจุบัน
J. Ouwehand

641

การเติมรายชื่อชั่วคราวนั้นเหมือนกับการดาวน์โหลดวิดีโอทั้งหมดในขณะที่การใช้งานyieldก็เหมือนกับการสตรีมวิดีโอนั้น


180
ฉันตระหนักดีว่าคำตอบนี้ไม่ใช่คำตอบทางเทคนิค แต่ฉันเชื่อว่าความคล้ายคลึงระหว่างอัตราผลตอบแทนและการสตรีมวิดีโอเป็นตัวอย่างที่ดีเมื่อทำความเข้าใจคำหลักผลตอบแทน ทุกอย่างทางเทคนิคได้รับการพูดเกี่ยวกับเรื่องนี้แล้วดังนั้นฉันพยายามที่จะอธิบาย "ในคำอื่น ๆ " มีกฎชุมชนที่บอกว่าคุณไม่สามารถอธิบายความคิดของคุณในแง่ที่ไม่ใช่ด้านเทคนิคได้หรือไม่?
anar khalilov

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

22
ยังคงเข้าใจแนวคิดและสิ่งนี้ช่วยให้มันโฟกัสได้ดีขึ้น
โทนี่

11
ฉันชอบคำตอบนี้ แต่ไม่ตอบคำถาม
Aneves

73

เป็นตัวอย่างแนวคิดเพื่อความเข้าใจเมื่อคุณควรใช้yieldสมมติว่าวิธีConsumeLoop()ประมวลผลรายการที่ส่งคืน / ให้โดยProduceList():

void ConsumeLoop() {
    foreach (Consumable item in ProduceList())        // might have to wait here
        item.Consume();
}

IEnumerable<Consumable> ProduceList() {
    while (KeepProducing())
        yield return ProduceExpensiveConsumable();    // expensive
}

หากไม่มีyieldการโทรไปยังProduceList()อาจใช้เวลานานเนื่องจากคุณต้องทำรายการให้เสร็จก่อนกลับมา:

//pseudo-assembly
Produce consumable[0]                   // expensive operation, e.g. disk I/O
Produce consumable[1]                   // waiting...
Produce consumable[2]                   // waiting...
Produce consumable[3]                   // completed the consumable list
Consume consumable[0]                   // start consuming
Consume consumable[1]
Consume consumable[2]
Consume consumable[3]

ใช้yieldมันจะจัดเรียงใหม่เรียงลำดับของการทำงาน "ในแบบคู่ขนาน":

//pseudo-assembly
Produce consumable[0]
Consume consumable[0]                   // immediately Consume
Produce consumable[1]
Consume consumable[1]                   // consume next
Produce consumable[2]
Consume consumable[2]                   // consume next
Produce consumable[3]
Consume consumable[3]                   // consume next

และสุดท้ายก่อนหน้านี้หลายคนแนะนำคุณควรใช้เวอร์ชัน 2 เพราะคุณมีรายการที่สมบูรณ์อยู่แล้ว


30

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

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

การใช้คำหลักที่อัตราผลตอบแทน (ใกล้ร็อบ Eisenburg ของCaliburn.Micro coroutinesการดำเนินงาน) ช่วยให้ฉันเพื่อแสดงความไม่ตรงกันโทรไปยังบริการเว็บเช่นนี้

public IEnumerable<IResult> HandleButtonClick() {
    yield return Show.Busy();

    var loginCall = new LoginResult(wsClient, Username, Password);
    yield return loginCall;
    this.IsLoggedIn = loginCall.Success;

    yield return Show.NotBusy();
}

สิ่งนี้จะทำคือเปิด BusyIndicator ของฉันโทรวิธีการเข้าสู่ระบบในบริการเว็บของฉันตั้งค่าสถานะ IsLoggedIn ของฉันเป็นค่าตอบแทนจากนั้นเปิด BusyIndicator กลับ

นี่คือวิธีการทำงาน: IResult มีวิธีการดำเนินการและเหตุการณ์ที่เสร็จสมบูรณ์ Caliburn.Micro คว้า IEnumerator จากการเรียกไปยัง HandleButtonClick () และส่งผ่านไปยังวิธี Coroutine.BeginExecute กระบวนการ BeginExecute เริ่มการวนซ้ำผ่าน IResults เมื่อ IResult แรกถูกส่งคืนการดำเนินการจะถูกหยุดชั่วคราวภายใน HandleButtonClick () และ BeginExecute () แนบตัวจัดการเหตุการณ์กับเหตุการณ์ที่เสร็จสมบูรณ์และเรียกใช้งาน Execute () IResult.Execute () สามารถทำงานได้ทั้งแบบซิงโครนัสหรือแบบอะซิงโครนัสและยิงเหตุการณ์ที่เสร็จสมบูรณ์เมื่อเสร็จสิ้น

LoginResult มีลักษณะดังนี้:

public LoginResult : IResult {
    // Constructor to set private members...

    public void Execute(ActionExecutionContext context) {
        wsClient.LoginCompleted += (sender, e) => {
            this.Success = e.Result;
            Completed(this, new ResultCompletionEventArgs());
        };
        wsClient.Login(username, password);
    }

    public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
    public bool Success { get; private set; }
}

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

หวังว่านี่จะช่วยให้ใครบางคนออกไป! ฉันสนุกกับการสำรวจวิธีการที่แตกต่างกันของผลตอบแทนที่สามารถใช้ได้


1
ตัวอย่างโค้ดของคุณเป็นตัวอย่างที่ยอดเยี่ยมเกี่ยวกับวิธีใช้ผลผลิตด้านนอกของบล็อก foreach หรือ foreach ตัวอย่างส่วนใหญ่แสดงผลตอบแทนคืนภายในตัววนซ้ำ มีประโยชน์มากเพราะฉันเพิ่งจะถามคำถามเกี่ยวกับ SO วิธีใช้อัตราผลตอบแทนนอกเหนือจากตัววนซ้ำ!
shelbypereira

ฉันไม่เคยใช้yieldวิธีนี้มาก่อนเลย ดูเหมือนว่าจะเป็นวิธีที่สง่างามในการเลียนแบบรูปแบบ async / await (ซึ่งฉันคิดว่าน่าจะถูกใช้แทนyieldหากนี่ถูกเขียนใหม่ในวันนี้) คุณพบว่าการใช้ความคิดสร้างสรรค์ของเหล่านี้yieldให้ผลตอบแทน (ไม่มีการเล่นสำนวนเจตนา) ลดผลตอบแทนในช่วงหลายปีที่ C # มีวิวัฒนาการมาตั้งแต่คุณตอบคำถามนี้? หรือคุณยังคงคิดวิธีการใช้งานที่ฉลาดเช่นนี้? และถ้าเป็นเช่นนั้นคุณจะแบ่งปันสถานการณ์ที่น่าสนใจให้เราอีกหรือไม่
ลำเอียง

27

นี้จะดูเหมือนเป็นข้อเสนอแนะที่แปลกประหลาด แต่ผมได้เรียนรู้วิธีการใช้yieldคำหลักใน C # โดยการอ่านนำเสนอเกี่ยวกับเครื่องกำเนิดไฟฟ้าในหลาม: เดวิดเอ็มบีซของhttp://www.dabeaz.com/generators/Generators.pdf คุณไม่จำเป็นต้องรู้ Python มากนักเพื่อทำความเข้าใจกับงานนำเสนอ - ฉันไม่ต้องการ ฉันพบว่ามันมีประโยชน์มากในการอธิบายไม่เพียง แต่กำเนิดว่าทำงานอย่างไร แต่ทำไมคุณควรดูแล


1
งานนำเสนอให้ภาพรวมอย่างง่าย รายละเอียดของวิธีการทำงานใน C # ถูกกล่าวถึงโดย Ray Chen ในลิงก์ที่stackoverflow.com/a/39507/939250 ลิงก์แรกอธิบายในรายละเอียดว่ามีวิธีการส่งคืนโดยปริยายที่สองในตอนท้ายของวิธีการคืนผลผลิต
Donal Lafferty

18

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

    static IEnumerable<Trip> CreatePossibleTrips()
    {
        for (int i = 0; i < 1000000; i++)
        {
            yield return new Trip
            {
                Id = i.ToString(),
                Driver = new Driver { Id = i.ToString() }
            };
        }
    }

จากนั้นวนซ้ำในแต่ละการเดินทาง:

    static void Main(string[] args)
    {
        foreach (var trip in CreatePossibleTrips())
        {
            // possible trip is actually calculated only at this point, because of yield
            if (IsTripGood(trip))
            {
                // match good trip
            }
        }
    }

หากคุณใช้รายการแทนผลผลิตคุณจะต้องจัดสรรวัตถุ 1 ล้านหน่วยไปยังหน่วยความจำ (~ 190mb) และตัวอย่างง่าย ๆ นี้จะใช้เวลา ~ 1400ms ในการเรียกใช้ อย่างไรก็ตามหากคุณใช้ผลตอบแทนคุณไม่จำเป็นต้องนำวัตถุ temp เหล่านี้ทั้งหมดไปยังหน่วยความจำและคุณจะได้รับความเร็วอัลกอริธึมที่เร็วขึ้นอย่างมาก: ตัวอย่างนี้จะใช้เวลาเพียง 400ms ในการรันโดยไม่ต้องใช้หน่วยความจำเลย


2
ภายใต้หน้าปกผลตอบแทนคืออะไร? ฉันคิดว่ามันเป็นรายการดังนั้นจะปรับปรุงการใช้หน่วยความจำได้อย่างไร
ม้วน

1
@ ม้วนyieldทำงานภายใต้การครอบคลุมโดยการใช้เครื่องสถานะภายใน นี่คือคำตอบ SO ด้วย 3 โพสต์บล็อกรายละเอียด MSDNที่อธิบายการใช้งานโดยละเอียด เขียนโดย Raymond Chen @ MSFT
Shiva

13

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

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

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


11

ผลตอบแทนมีประโยชน์อย่างมากสองอย่าง

ช่วยในการจัดทำซ้ำที่กำหนดเองโดยไม่สร้างคอลเลกชันชั่วคราว (กำลังโหลดข้อมูลและการวนซ้ำทั้งหมด)

มันช่วยในการย้ำซ้ำซาก (สตรีมมิ่ง)

ด้านล่างเป็นวิดีโอง่ายๆที่ฉันได้สร้างขึ้นด้วยการสาธิตเต็มรูปแบบเพื่อสนับสนุนสองจุดข้างต้น

http://www.youtube.com/watch?v=4fju3xcm21M


10

นี่คือสิ่งที่Chris Sellsบอกเกี่ยวกับข้อความเหล่านั้นในภาษา C # Programming ;

บางครั้งฉันลืมไปว่าการคืนผลตอบแทนนั้นไม่เหมือนกับผลตอบแทนในโค้ดนั้นหลังจากสามารถส่งคืนผลผลิตได้ ตัวอย่างเช่นโค้ดหลังจากการส่งคืนแรกที่นี่จะไม่สามารถดำเนินการ:

    int F() {
return 1;
return 2; // Can never be executed
}

ในทางกลับกันโค้ดหลังจากที่ได้รับผลตอบแทนที่หนึ่งครั้งแรกที่นี่สามารถดำเนินการได้:

IEnumerable<int> F() {
yield return 1;
yield return 2; // Can be executed
}

สิ่งนี้มักจะกัดฉันในคำสั่ง if:

IEnumerable<int> F() {
if(...) { yield return 1; } // I mean this to be the only
// thing returned
yield return 2; // Oops!
}

ในกรณีเหล่านี้การจดจำว่าการคืนผลตอบแทนไม่ใช่ "ขั้นสุดท้าย" เช่นการกลับมามีประโยชน์


เพื่อลดความคลุมเครือโปรดอธิบายให้ชัดเจนเมื่อคุณพูดว่าเป็นไปได้หรือไม่? เป็นไปได้ไหมที่คนแรกจะกลับมาใหม่และไม่ได้ผลตอบแทนครั้งที่สอง?
Johno Crawford

@JohnoCrawford คำสั่งให้ผลตอบแทนที่สองจะดำเนินการก็ต่อเมื่อค่าที่สอง / ถัดไปของ IEnumerable ถูกแจกแจง เป็นไปได้อย่างสิ้นเชิงว่าจะไม่เป็นเช่นF().Any()นี้จะกลับมาหลังจากพยายามระบุผลลัพธ์แรกเท่านั้น โดยทั่วไปคุณไม่ควรพึ่งการIEnumerable yieldเปลี่ยนแปลงสถานะของโปรแกรมเพราะอาจไม่ได้รับการกระตุ้น
Zac Faragher

8

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

ตัวอย่างที่สองคือการแปลงตัวแจงนับ / iterator เป็นรายการที่มีวิธีการ ToList () ซึ่งหมายความว่าจะวนซ้ำทุกรายการในตัวแจงนับเองแล้วส่งกลับรายการแบบเรียบ


8

นี่เป็นประเด็นนอกเหนือจากประเด็น แต่เนื่องจากคำถามถูกติดแท็กแนวปฏิบัติที่ดีที่สุดฉันจะเดินหน้าต่อไปและโยนสองเซ็นต์ของฉัน สำหรับสิ่งประเภทนี้ฉันชอบที่จะทำให้มันเป็นทรัพย์สิน:

public static IEnumerable<Product> AllProducts
{
    get {
        using (AdventureWorksEntities db = new AdventureWorksEntities()) {
            var products = from product in db.Product
                           select product;

            return products;
        }
    }
}

แน่นอนว่ามันเป็นจานหม้อไอน้ำเล็ก ๆ น้อย ๆ แต่รหัสที่ใช้สิ่งนี้จะดูสะอาดกว่ามาก:

prices = Whatever.AllProducts.Select (product => product.price);

VS

prices = Whatever.GetAllProducts().Select (product => product.price);

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


7

แล้วเรื่องนี้ล่ะ

public static IEnumerable<Product> GetAllProducts()
{
    using (AdventureWorksEntities db = new AdventureWorksEntities())
    {
        var products = from product in db.Product
                       select product;

        return products.ToList();
    }
}

ฉันเดาว่านี่สะอาดกว่ามาก ฉันไม่มี VS2008 ในมือเพื่อตรวจสอบแม้ว่า ไม่ว่าในกรณีใดหากผลิตภัณฑ์ใช้ IEnumerable (ตามที่ดูเหมือน - ใช้ในคำสั่ง foreach) ฉันจะส่งคืนโดยตรง


2
โปรดแก้ไข OP เพื่อรวมข้อมูลเพิ่มเติมแทนที่จะโพสต์คำตอบ
Brian Rasmussen

คุณต้องบอกฉันว่า OP ทำอะไรได้บ้างสำหรับ :-) ขอบคุณ
petr k

โพสต์ต้นฉบับฉันคิดว่า ฉันไม่สามารถแก้ไขโพสต์ดังนั้นนี่จึงเป็นวิธีที่จะไป
petr k.

5

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

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

ตัวอย่างบางส่วนที่สามารถใช้ผลตอบแทนได้คือ:

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

เพื่อตอบคำถามของคุณฉันจะใช้รุ่น 2


3

ส่งคืนรายการโดยตรง ประโยชน์ที่ได้รับ:

  • ชัดเจนยิ่งขึ้น
  • รายการนี้สามารถใช้ซ้ำได้ (ตัววนซ้ำไม่ได้)ไม่จริงขอบคุณจอน

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


อย่าลืมว่ามันส่งคืนใน IEnumerable <T> ไม่ใช่ IEnumerator <T> - คุณสามารถโทรหา GetEnumerator อีกครั้ง
Jon Skeet

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

1

keyphrase ที่ให้ผลตอบแทนถูกใช้เพื่อดูแลเครื่องสถานะสำหรับการรวบรวมเฉพาะ เมื่อใดก็ตามที่ CLR เห็น keyphrase ที่ให้ผลตอบแทนถูกใช้ CLR จะใช้รูปแบบตัวแจงนับกับส่วนของรหัสนั้น การใช้งานประเภทนี้ช่วยให้นักพัฒนาจากทุกประเภทของการประปาที่เราจะต้องทำในกรณีที่ไม่มีคำหลัก

สมมติว่านักพัฒนากรองคอลเลกชันบางส่วนวนซ้ำคอลเลกชันแล้วแยกวัตถุเหล่านั้นในคอลเล็กชันใหม่ การประปาชนิดนี้ค่อนข้างน่าเบื่อ

เพิ่มเติมเกี่ยวกับคำหลักที่นี่ที่บทความนี้


-4

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

ผลผลิตมีสองประโยชน์:

  1. คุณไม่จำเป็นต้องอ่านค่าเหล่านี้สองครั้ง
  2. คุณสามารถรับโหนดย่อยจำนวนมาก แต่ไม่จำเป็นต้องใส่โหนดทั้งหมดในหน่วยความจำ

มีคำอธิบายที่ชัดเจนอีกอย่างหนึ่งที่อาจช่วยคุณได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.