Năng suất làm gì phá vỡ; làm gì trong C #?


494

Tôi đã thấy cú pháp này trong MSDN : yield break, nhưng tôi không biết nó làm gì. Có ai biết không?


26
Tuy nhiên, tài liệu MSDN đề cập đến nó nhưng không giải thích nó. Đó là một cách tồi để trêu chọc một nhà phát triển đói kiến ​​thức.
Phil

3
Lợi nhuận mang lại giúp loại bỏ sự cần thiết của một danh sách sao lưu, đó là bạn không cần mã hóa một cái gì đó giống như MyList.Add(...)chỉ cần làm yield return .... Nếu bạn cần sớm thoát ra khỏi vòng lặp và trả lại danh sách sao lưu ảo mà bạn sử dụngyield break;
Người đàn ông Muffin

Câu trả lời:


529

Nó chỉ định rằng một trình vòng lặp đã kết thúc. Bạn có thể nghĩ về yield breakmột returntuyên bố không trả về giá trị.

Ví dụ: nếu bạn định nghĩa một hàm là một trình vòng lặp, phần thân của hàm có thể trông như thế này:

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

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

Lưu ý rằng sau khi vòng lặp hoàn thành tất cả các chu kỳ của nó, dòng cuối cùng sẽ được thực thi và bạn sẽ thấy thông báo trong ứng dụng bảng điều khiển của mình.

Hoặc như thế này với 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");

Trong trường hợp này, câu lệnh cuối cùng không bao giờ được thực thi vì chúng ta đã rời hàm sớm.


8
Nó có thể chỉ đơn giản là breakthay vì yield breaktrong ví dụ của bạn ở trên? Trình biên dịch không phàn nàn về điều đó.
orad

85
@orad một cách đơn giản breaktrong trường hợp này sẽ dừng vòng lặp, nhưng nó sẽ không hủy bỏ việc thực thi phương thức, do đó dòng cuối cùng sẽ được thực thi và "Không nhìn thấy văn bản của tôi" sẽ thực sự được nhìn thấy.
Damir Zekić

5
@Damir Zekić Bạn cũng có thể thêm vào câu trả lời của mình tại sao bạn nên thích lợi nhuận hơn so với lợi nhuận và sự khác biệt từ hai loại này là gì?
Bruno Costa

11
@ DamirZekić trả về null và phá vỡ sản lượng không giống nhau. Nếu bạn trả về null thì bạn có thể có NullReferenceExceptionđược một điều tra viên của IEnumerable, trong khi với mức giảm năng suất thì bạn không (có một trường hợp không có phần tử nào).
Bruno Costa

6
@BrunoCosta Cố gắng có lợi nhuận thường xuyên trong cùng một phương thức sẽ gây ra lỗi trình biên dịch. Sử dụng yield return xcảnh báo trình biên dịch bạn muốn phương thức này là đường cú pháp để tạo một Enumeratorđối tượng. Điều này có phương pháp MoveNext()và tài sản Current. MoveNext () thực thi phương thức cho đến khi một yield returncâu lệnh và biến giá trị đó thành Current. Lần tiếp theo MoveNext được gọi, thực thi tiếp tục từ đó. yield breakđặt Hiện tại thành null, báo hiệu sự kết thúc của điều tra viên này, do đó foreach (var x in myEnum())sẽ kết thúc.
Wolfzoon

57

Kết thúc một khối lặp (ví dụ: không có thêm phần tử nào trong IEnumerable).


2
với [+1] - mặc dù về mặt học thuật không có trình vòng lặp trong .NET. chỉ các điều tra viên (một hướng, về phía trước.)
Shaun Wilson

@Shaun Wilson, nhưng với yieldtừ khóa, bạn có thể lặp bộ sưu tập theo cả hai hướng, hơn nữa bạn có thể lấy mọi yếu tố tiếp theo của bộ sưu tập không liên tiếp
monstr

@monstr bạn có thể lặp lại bộ sưu tập theo bất kỳ cách nào và bạn không cần yieldphải làm điều đó. Tôi không nói bạn sai, tôi thấy những gì bạn đang đề xuất; nhưng, về mặt học thuật không có trình lặp trong .NET, chỉ có các điều tra viên (một hướng, chuyển tiếp) - không giống như stdc ++, không có "khung lặp chung chung" được định nghĩa trong CTS / CLR. LINQ giúp thu hẹp khoảng cách với các phương thức mở rộng sử dụng yield return và cả phương thức gọi lại , nhưng chúng là phương thức mở rộng hạng nhất, không phải là trình vòng lặp hạng nhất. kết quả IEnumerablekhông thể tự lặp đi lặp lại theo bất kỳ hướng nào ngoài việc chuyển tiếp người gọi.
Shaun Wilson

29

Nói với iterator rằng nó đã kết thúc.

Ví dụ:

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

yieldvề cơ bản làm cho một IEnumerable<T>phương thức hoạt động tương tự như một luồng theo lịch trình hợp tác (trái ngược với ưu tiên).

yield returngiống như một luồng gọi hàm "lịch trình" hoặc "ngủ" để từ bỏ quyền kiểm soát CPU. Giống như một luồng, IEnumerable<T>phương thức lấy lại các điều khiển tại điểm ngay sau đó, với tất cả các biến cục bộ có cùng giá trị như trước khi điều khiển bị từ bỏ.

yield break giống như một luồng đến cuối chức năng của nó và chấm dứt.

Mọi người nói về một "máy trạng thái", nhưng một máy trạng thái thực sự là một "luồng". Một luồng có một số trạng thái (giá trị Ie của các biến cục bộ) và mỗi lần nó được lên lịch, nó sẽ thực hiện một số hành động để đạt đến trạng thái mới. Điểm mấu chốt yieldlà, không giống như các luồng hệ điều hành mà chúng ta đã sử dụng, mã sử dụng nó bị đóng băng kịp thời cho đến khi phép lặp được nâng cao hoặc kết thúc bằng tay.



9

Các yield breaktuyên bố gây ra liệt kê để dừng lại. Trong thực tế, yield breakhoàn thành việc liệt kê mà không trả lại bất kỳ mục bổ sung.

Hãy xem xét rằng thực sự có hai cách mà một phương thức lặp có thể dừng lặp lại. Trong một trường hợp, logic của phương thức có thể tự nhiên thoát khỏi phương thức sau khi trả về tất cả các mục. Đây là một ví dụ:

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.");
}

Trong ví dụ trên, phương thức lặp sẽ tự nhiên dừng thực thi một khi các maxCountsố nguyên tố đã được tìm thấy.

Các yield break tuyên bố là một cách khác để iterator ngừng đếm. Đó là một cách để thoát ra khỏi sự liệt kê sớm. Đây là phương pháp tương tự như trên. Lần này, phương thức có giới hạn về lượng thời gian mà phương thức có thể thực thi.

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.");
}

Chú ý cuộc gọi đến yield break . Trong thực tế, nó là thoát khỏi liệt kê sớm.

Cũng lưu ý rằng các yield breakcông trình khác nhau hơn chỉ là một đồng bằng break. Trong ví dụ trên, yield breakthoát khỏi phương thức mà không thực hiện cuộc gọi đến Debug.WriteLine(..).


7

Tại đây http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ là ví dụ rất hay:

công khai IEn Countable <int> Range (int min, int max)
{
   trong khi (đúng)
   {
      nếu (tối thiểu> = tối đa)
      {
         phá vỡ năng suất;
      }
      lợi nhuận tối thiểu ++;
   }
}

và giải thích rằng nếu một yield breakcâu lệnh được nhấn trong một phương thức, thì việc thực thi phương thức đó dừng lại mà không trả về. Có một số tình huống thời gian, khi bạn không muốn đưa ra bất kỳ kết quả nào, thì bạn có thể sử dụng phá vỡ năng suất.


5

phá vỡ lợi nhuận chỉ là một cách nói trả lại lần cuối và không trả lại bất kỳ giá trị nào

ví dụ

// 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

Nếu ý của bạn là "điều gì làm giảm năng suất thực sự làm", thì "nó hoạt động như thế nào" - Xem blog của Raymond Chen để biết chi tiết https://devbloss.microsoft.com/oldnewthing/20080812-00/?p=21273

Cter iterators tạo ra một số mã rất phức tạp.


12
Chà, chúng chỉ phức tạp nếu bạn quan tâm đến mã được biên dịch. Mã sử ​​dụng chúng khá đơn giản.
Robert Rossney

@Robert, đó là những gì tôi muốn nói nên tôi đã cập nhật câu trả lời để đưa bình luận của bạn
Tony Lee

-2

Từ khóa suất được sử dụng cùng với từ khóa return để cung cấp giá trị cho đối tượng liệt kê. lợi nhuận chỉ định giá trị, hoặc giá trị, được trả lại. Khi đạt được tuyên bố lợi nhuận, vị trí hiện tại được lưu trữ. Việc thực thi được khởi động lại từ vị trí này vào lần tiếp theo được gọi.

Để giải thích ý nghĩa bằng cách sử dụng một ví dụ:

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

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

Các giá trị được trả về khi giá trị này được lặp lại là: 0, 2, 5.

Điều quan trọng cần lưu ý là biến đếm trong ví dụ này là biến cục bộ. Sau lần lặp thứ hai trả về giá trị là 2, lần lặp thứ ba bắt đầu từ nơi nó còn lại trước đó, trong khi vẫn giữ giá trị trước đó của biến cục bộ có tên là bộ đếm là 2.


11
Bạn đã không giải thích gì yield breakkhông
Jay Sullivan

Tôi không nghĩ yield returnthực sự hỗ trợ trả về nhiều giá trị. Có thể đó không phải là những gì bạn thực sự muốn nói, nhưng đó là cách tôi đọc nó.
Sam

Sam - phương thức SampleNumbers với nhiều câu lệnh trả về lợi nhuận thực tế hoạt động, giá trị của trình lặp được trả về ngay lập tức và thực thi được tiếp tục khi giá trị tiếp theo được yêu cầu. Tôi đã thấy mọi người kết thúc một phương pháp như thế này bằng "phá vỡ lợi nhuận", nhưng nó không cần thiết. Đánh vào cuối phương thức cũng kết thúc trình lặp
Loren Paulsen

Lý do đây là một ví dụ kém yield breaklà vì nó không chứa một trình liệt kê cấp ngôn ngữ, chẳng hạn như foreach- khi sử dụng một điều tra viên, yield breakcung cấp giá trị thực. ví dụ này trông giống như một vòng lặp không được kiểm soát. bạn gần như sẽ không bao giờ thấy mã này trong thế giới thực (tất cả chúng ta đều có thể nghĩ về một số trường hợp cạnh, chắc chắn), không có "iterator" ở đây. "khối lặp" không thể vượt ra ngoài phương thức như một vấn đề đặc tả ngôn ngữ. những gì thực sự được trả lại là một "vô số", xem thêm: stackoverflow.com/questions/742497/ Kẻ
Shaun Wilson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.