Từ khóa năng suất được sử dụng trong C # là gì?


828

Trong phần Làm thế nào tôi có thể chỉ đưa ra một đoạn của IList <> câu hỏi, một trong những câu trả lời có đoạn mã sau:

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}

Từ khóa năng suất làm gì ở đó? Tôi đã thấy nó được tham chiếu ở một vài nơi, và một câu hỏi khác, nhưng tôi hoàn toàn không biết nó thực sự làm gì. Tôi đã từng nghĩ đến năng suất theo nghĩa của một chủ đề mang lại cho một chủ đề khác, nhưng điều đó dường như không liên quan ở đây.


Chỉ cần MSDN link về nó là ở đây msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
Developer

14
Điều này không đáng ngạc nhiên. Sự nhầm lẫn xuất phát từ thực tế là chúng ta có điều kiện để xem "return" là đầu ra của hàm trong khi trước "năng suất" thì không.
Larry

4
Tôi đã đọc tài liệu nhưng tôi sợ tôi vẫn không hiểu :(
Ortund

Câu trả lời:


737

Các yieldtừ khóa thực sự làm khá nhiều ở đây.

Hàm trả về một đối tượng thực hiện IEnumerable<object>giao diện. Nếu một chức năng gọi bắt đầu foreaching trên đối tượng này, chức năng được gọi lại cho đến khi nó "nhường". Đây là đường cú pháp được giới thiệu trong C # 2.0 . Trong các phiên bản trước, bạn phải tạo riêng IEnumerableIEnumeratorcác đối tượng để làm những thứ như thế này.

Cách dễ nhất để hiểu mã như thế này là nhập một ví dụ, đặt một số điểm dừng và xem điều gì sẽ xảy ra. Hãy thử bước qua ví dụ này:

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

Khi bạn bước qua ví dụ, bạn sẽ tìm thấy cuộc gọi đầu tiên để Integers()trả về 1. Cuộc gọi thứ hai trả về 2và đường dây yield return 1không được thực hiện lại.

Đây là một ví dụ thực tế:

public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
    using (var connection = CreateConnection())
    {
        using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
        {
            command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return make(reader);
                }
            }
        }
    }
}

113
Trong trường hợp này sẽ dễ dàng hơn, tôi chỉ sử dụng số nguyên ở đây để hiển thị cách hoạt động của lợi nhuận. Những điều tốt đẹp về việc sử dụng lợi nhuận là đó là một cách rất nhanh để thực hiện mẫu lặp, vì vậy mọi thứ được đánh giá một cách lười biếng.
Mendelt

111
Cũng đáng lưu ý bạn có thể sử dụng yield break;khi bạn không muốn trả lại bất kỳ mục nào nữa.
Rory

7
yieldkhông phải là một từ khóa. Nếu là vậy thì tôi không thể sử dụng sản lượng như một định danh như trongint yield = 500;
Brandin

5
@Brandin đó là vì tất cả các ngôn ngữ lập trình đều hỗ trợ hai loại từ khóa là dành riêng và theo ngữ cảnh. năng suất rơi vào loại sau đó là lý do tại sao mã của bạn không bị cấm bởi trình biên dịch C #. Thêm chi tiết tại đây: ericlippert.com/2009/05/11/reserved-and-contextual-keywords Bạn sẽ vui mừng khi biết rằng cũng có những từ dành riêng không được công nhận là từ khóa bởi một ngôn ngữ. Ví dụ như goto trong java. Thêm chi tiết tại đây: stackoverflow.com/questions/2545103/
Mạnh

7
'If a calling function starts foreach-ing over this object the function is called again until it "yields"'. không đúng với tôi Tôi luôn nghĩ về từ khóa c # ield trong bối cảnh "vụ mùa mang lại một vụ mùa bội thu", thay vì "chiếc xe mang lại cho người đi bộ".
Zack

369

Lặp lại. Nó tạo ra một máy trạng thái "dưới vỏ bọc" để nhớ bạn đã ở đâu trên mỗi chu kỳ bổ sung của chức năng và chọn ra từ đó.


210

Năng suất có hai công dụng tuyệt vời,

  1. Nó giúp cung cấp lặp lại tùy chỉnh mà không cần tạo bộ sưu tập tạm thời.

  2. Nó giúp làm lặp đi lặp lại trạng thái. nhập mô tả hình ảnh ở đây

Để giải thích rõ hơn hai điểm trên, tôi đã tạo một video đơn giản mà bạn có thể xem tại đây


13
Các video giúp tôi hiểu rõ ràng yield. @ ShivprasadKoirala's bài viết dự án mã của C # Yield là gì? cùng một lời giải thích cũng là một nguồn tốt
Dush

Tôi cũng sẽ thêm một điểm thứ ba yieldlà cách "nhanh" để tạo một IEnumerator tùy chỉnh (thay vì có một lớp thực hiện giao diện IEnumerator).
MrTourkos

Tôi đã xem video Shivprasad của bạn và nó đã khám phá rõ ràng việc sử dụng từ khóa suất.
Xé Aurstad

Cảm ơn về video!. Giải thích rất tốt!
Roblogic

Video tuyệt vời, nhưng tự hỏi ... Việc triển khai sử dụng năng suất rõ ràng là sạch hơn, nhưng về cơ bản nó phải tạo bộ nhớ tạm thời hoặc / và Liệt kê nội bộ để theo dõi trạng thái (hay đúng hơn là tạo một máy trạng thái). Vì vậy, "Yield" có làm gì khác hơn là làm cho việc thực hiện đơn giản hơn và làm cho mọi thứ trở nên tốt hơn hoặc có điều gì khác với nó không? Làm thế nào về hiệu quả, là chạy mã bằng cách sử dụng Yield nhiều hơn hoặc ít hiệu quả hơn / nhanh hơn mà không có?
khó khăn Câu hỏi

135

Gần đây Raymond Chen cũng đã chạy một loạt các bài viết thú vị về từ khóa suất.

Mặc dù nó thường được sử dụng để dễ dàng thực hiện một mẫu lặp, nhưng có thể được khái quát thành một máy trạng thái. Không có điểm nào trong việc trích dẫn Raymond, phần cuối cùng cũng liên kết đến các mục đích sử dụng khác (nhưng ví dụ trong blog của Entin là đặc biệt tốt, cho thấy cách viết mã an toàn không đồng bộ).


Điều này cần phải được bỏ phiếu. Ngọt ngào như thế nào anh giải thích mục đích của các nhà điều hành và nội bộ.
sajidnizami

3
phần 1 giải thích đường cú pháp của "lợi nhuận". giải thích tuyệt vời!
Dror Weiss

99

Ngay từ cái nhìn đầu tiên, lợi nhuận mang lại là một đường .NET để trả về một IEnumerable .

Không có năng suất, tất cả các mục của bộ sưu tập được tạo cùng một lúc:

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        return new List<SomeData> {
            new SomeData(), 
            new SomeData(), 
            new SomeData()
        };
    }
}

Cùng một mã sử dụng năng suất, nó trả về từng mục:

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        yield return new SomeData();
        yield return new SomeData();
        yield return new SomeData();
    }
}

Ưu điểm của việc sử dụng năng suất là nếu hàm tiêu thụ dữ liệu của bạn chỉ cần mục đầu tiên của bộ sưu tập, phần còn lại của các mục sẽ không được tạo.

Toán tử năng suất cho phép tạo ra các mặt hàng theo yêu cầu. Đó là một lý do tốt để sử dụng nó.


40

yield returnđược sử dụng với các điều tra viên. Trên mỗi lệnh gọi của báo cáo sản lượng, quyền điều khiển được trả về cho người gọi nhưng nó đảm bảo rằng trạng thái của callee được duy trì. Do đó, khi người gọi liệt kê phần tử tiếp theo, nó tiếp tục thực thi trong phương thức callee từ câu lệnh ngay sau yieldcâu lệnh.

Hãy để chúng tôi cố gắng hiểu điều này với một ví dụ. Trong ví dụ này, tương ứng với mỗi dòng tôi đã đề cập đến thứ tự thực hiện luồng.

static void Main(string[] args)
{
    foreach (int fib in Fibs(6))//1, 5
    {
        Console.WriteLine(fib + " ");//4, 10
    }            
}

static IEnumerable<int> Fibs(int fibCount)
{
    for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
    {
        yield return prevFib;//3, 9
        int newFib = prevFib + currFib;//6
        prevFib = currFib;//7
        currFib = newFib;//8
    }
}

Ngoài ra, nhà nước được duy trì cho mỗi liệt kê. Giả sử, tôi có một lệnh gọi Fibs()phương thức khác thì trạng thái sẽ được đặt lại cho nó.


2
đặt trướcFibib = 1 - số Fibonacci đầu tiên là "1", không phải là "0"
fubo

31

Theo trực giác, từ khóa trả về một giá trị từ hàm mà không để lại nó, tức là trong ví dụ mã của bạn, nó trả về itemgiá trị hiện tại và sau đó tiếp tục vòng lặp. Chính thức hơn, nó được trình biên dịch sử dụng để tạo mã cho một trình vòng lặp . Lặp lại là các hàm trả về IEnumerablecác đối tượng. Các MSDN có một số bài viết về họ.


4
Chà, chính xác là nó không tiếp tục vòng lặp, nó tạm dừng nó cho đến khi cha mẹ gọi "iterator.next ()".
Alex

8
@jitbit Đó là lý do tại sao tôi sử dụng trực giác và trực tiếp nhiều hơn nữa.
Konrad Rudolph

31

Một danh sách hoặc thực hiện mảng tải tất cả các mục ngay lập tức trong khi thực hiện năng suất cung cấp một giải pháp thực hiện hoãn lại.

Trong thực tế, thường mong muốn thực hiện số lượng công việc tối thiểu khi cần thiết để giảm mức tiêu thụ tài nguyên của một ứng dụng.

Ví dụ: chúng tôi có thể có một ứng dụng xử lý hàng triệu bản ghi từ cơ sở dữ liệu. Những lợi ích sau đây có thể đạt được khi chúng ta sử dụng IEnumerable trong mô hình dựa trên kéo thực thi bị trì hoãn:

  • Khả năng mở rộng, độ tin cậy và khả năng dự đoán có khả năng cải thiện do số lượng hồ sơ không ảnh hưởng đáng kể đến các yêu cầu tài nguyên của ứng dụng.
  • Hiệu suất và khả năng đáp ứng có khả năng cải thiện do quá trình xử lý có thể bắt đầu ngay lập tức thay vì chờ toàn bộ bộ sưu tập được tải trước.
  • Khả năng phục hồi và sử dụng có khả năng cải thiện do ứng dụng có thể bị dừng, khởi động, gián đoạn hoặc thất bại. Chỉ các mục trong tiến trình sẽ bị mất so với tìm nạp trước tất cả dữ liệu trong đó chỉ sử dụng một phần kết quả đã thực sự được sử dụng.
  • Có thể xử lý liên tục trong các môi trường có luồng khối lượng công việc không đổi được thêm vào.

Dưới đây là so sánh giữa xây dựng bộ sưu tập trước tiên như danh sách so với sử dụng năng suất.

Danh sách ví dụ

    public class ContactListStore : IStore<ContactModel>
    {
        public IEnumerable<ContactModel> GetEnumerator()
        {
            var contacts = new List<ContactModel>();
            Console.WriteLine("ContactListStore: Creating contact 1");
            contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
            Console.WriteLine("ContactListStore: Creating contact 2");
            contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
            Console.WriteLine("ContactListStore: Creating contact 3");
            contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
            return contacts;
        }
    }

    static void Main(string[] args)
    {
        var store = new ContactListStore();
        var contacts = store.GetEnumerator();

        Console.WriteLine("Ready to iterate through the collection.");
        Console.ReadLine();
    }

Bảng điều khiển Đầu ra
ContactListStore: Tạo liên hệ 1
ContactListStore: Tạo liên hệ 2
ContactListStore: Tạo liên hệ 3
Sẵn sàng lặp qua bộ sưu tập.

Lưu ý: Toàn bộ bộ sưu tập đã được tải vào bộ nhớ mà không yêu cầu một mục nào trong danh sách

Ví dụ về năng suất

public class ContactYieldStore : IStore<ContactModel>
{
    public IEnumerable<ContactModel> GetEnumerator()
    {
        Console.WriteLine("ContactYieldStore: Creating contact 1");
        yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
        Console.WriteLine("ContactYieldStore: Creating contact 2");
        yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
        Console.WriteLine("ContactYieldStore: Creating contact 3");
        yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
    }
}

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();

    Console.WriteLine("Ready to iterate through the collection.");
    Console.ReadLine();
}

Bảng điều khiển đầu ra
Sẵn sàng để lặp qua bộ sưu tập.

Lưu ý: Bộ sưu tập hoàn toàn không được thực hiện. Điều này là do bản chất "thực thi hoãn lại" của IEnumerable. Xây dựng một mục sẽ chỉ xảy ra khi nó thực sự cần thiết.

Chúng ta hãy gọi lại bộ sưu tập và phản đối hành vi khi chúng ta tìm liên hệ đầu tiên trong bộ sưu tập.

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();
    Console.WriteLine("Ready to iterate through the collection");
    Console.WriteLine("Hello {0}", contacts.First().FirstName);
    Console.ReadLine();
}

Bảng điều khiển đầu ra
Sẵn sàng lặp lại qua bộ sưu tập
ContactYieldStore: Tạo liên hệ 1
Xin chào Bob

Đẹp! Chỉ tiếp xúc đầu tiên được xây dựng khi khách hàng "kéo" vật phẩm ra khỏi bộ sưu tập.


1
Câu trả lời này cần được chú ý nhiều hơn! Thx
leon22

@ leon22 hoàn toàn +2
snr

26

Đây là một cách đơn giản để hiểu khái niệm: Ý tưởng cơ bản là, nếu bạn muốn có một bộ sưu tập mà bạn có thể sử dụng "foreach ", nhưng việc thu thập các mục vào bộ sưu tập rất tốn kém vì một số lý do (như truy vấn chúng ra khỏi cơ sở dữ liệu), VÀ bạn thường sẽ không cần toàn bộ bộ sưu tập, sau đó bạn tạo một hàm xây dựng bộ sưu tập một mục tại một thời điểm và đưa nó trở lại cho người tiêu dùng (người sau đó có thể chấm dứt nỗ lực thu thập sớm).

Hãy nghĩ về nó theo cách này: Bạn đến quầy thịt và muốn mua một pound giăm bông. Người bán thịt lấy một cái giăm bông 10 pound ra phía sau, đặt nó vào máy thái lát, cắt lát toàn bộ, sau đó mang đống bánh lại cho bạn và đo ra một pound của nó. (Cách cũ). Với yield, người bán thịt mang máy thái thịt đến quầy, và bắt đầu cắt và "nhường" từng lát lên bàn cân cho đến khi nó đo được 1 pound, sau đó bọc lại cho bạn và bạn đã hoàn thành. Old Way có thể tốt hơn cho người bán thịt (cho phép anh ta tổ chức máy móc của mình theo cách anh ta thích), nhưng New Way rõ ràng hiệu quả hơn trong hầu hết các trường hợp cho người tiêu dùng.


18

Các yieldtừ khóa cho phép bạn tạo ra một IEnumerable<T>trong các hình thức trên một khối iterator . Khối lặp này hỗ trợ thực thi hoãn lại và nếu bạn không quen với khái niệm này, nó có thể xuất hiện gần như kỳ diệu. Tuy nhiên, vào cuối ngày, nó chỉ là mã thực thi mà không có bất kỳ thủ đoạn kỳ lạ nào.

Một khối lặp có thể được mô tả là đường cú pháp trong đó trình biên dịch tạo ra một máy trạng thái theo dõi mức độ liệt kê của liệt kê đã tiến triển đến đâu. Để liệt kê một vô số, bạn thường sử dụng một foreachvòng lặp. Tuy nhiên, mộtforeach vòng lặp cũng là cú pháp đường. Vì vậy, bạn có hai khái niệm trừu tượng bị xóa khỏi mã thực, đó là lý do ban đầu có thể khó hiểu làm thế nào tất cả hoạt động cùng nhau.

Giả sử rằng bạn có một khối lặp rất đơn giản:

IEnumerable<int> IteratorBlock()
{
    Console.WriteLine("Begin");
    yield return 1;
    Console.WriteLine("After 1");
    yield return 2;
    Console.WriteLine("After 2");
    yield return 42;
    Console.WriteLine("End");
}

Các khối lặp thực thường có các điều kiện và các vòng lặp nhưng khi bạn kiểm tra các điều kiện và hủy kiểm soát các vòng lặp thì chúng vẫn kết thúc như yield câu lệnh xen kẽ với các mã khác.

Để liệt kê khối foreachlặp, một vòng lặp được sử dụng:

foreach (var i in IteratorBlock())
    Console.WriteLine(i);

Đây là đầu ra (không có gì ngạc nhiên ở đây):

Bắt đầu
1
Sau 1
2
Sau 2
42
Kết thúc

Như đã nêu ở trên foreachlà cú pháp đường:

IEnumerator<int> enumerator = null;
try
{
    enumerator = IteratorBlock().GetEnumerator();
    while (enumerator.MoveNext())
    {
        var i = enumerator.Current;
        Console.WriteLine(i);
    }
}
finally
{
    enumerator?.Dispose();
}

Trong một nỗ lực gỡ rối điều này, tôi đã đưa ra một sơ đồ trình tự với các tóm tắt được loại bỏ:

Sơ đồ trình tự khối C # iterator

Máy trạng thái được tạo bởi trình biên dịch cũng thực hiện trình liệt kê nhưng để làm cho sơ đồ rõ ràng hơn, tôi đã chỉ ra chúng như các trường hợp riêng biệt. (Khi máy trạng thái được liệt kê từ một luồng khác, bạn thực sự có các trường hợp riêng biệt nhưng chi tiết đó không quan trọng ở đây.)

Mỗi khi bạn gọi khối lặp của mình, một phiên bản mới của máy trạng thái được tạo. Tuy nhiên, không có mã nào của bạn trong khối iterator được thực thi cho đến khi enumerator.MoveNext()thực thi lần đầu tiên. Đây là cách thực hiện trì hoãn hoạt động. Đây là một ví dụ (khá ngớ ngẩn):

var evenNumbers = IteratorBlock().Where(i => i%2 == 0);

Tại thời điểm này, trình vòng lặp chưa được thực thi. Các Wherekhoản tạo ra một mới IEnumerable<T>mà kết thúc tốt đẹp các IEnumerable<T>trả về bởi IteratorBlocknhưng đếm này vẫn chưa được liệt kê. Điều này xảy ra khi bạn thực hiện một foreachvòng lặp:

foreach (var evenNumber in evenNumbers)
    Console.WriteLine(eventNumber);

Nếu bạn liệt kê số đếm hai lần thì một phiên bản mới của máy trạng thái được tạo ra mỗi lần và khối lặp của bạn sẽ thực thi cùng một mã hai lần.

Chú ý rằng phương pháp LINQ thích ToList(), ToArray(), First(), Count()vv sẽ sử dụng một foreachvòng lặp để liệt kê các đếm được. Ví dụ, ToList()sẽ liệt kê tất cả các yếu tố của vô số và lưu trữ chúng trong một danh sách. Bây giờ bạn có thể truy cập vào danh sách để có được tất cả các phần tử có thể đếm được mà không cần khối lặp lại thực thi lại. Có một sự đánh đổi giữa việc sử dụng CPU để tạo ra các phần tử của vô số lần và bộ nhớ để lưu trữ các phần tử của phép liệt kê để truy cập chúng nhiều lần khi sử dụng các phương thức như thế nào ToList().


18

Nếu tôi hiểu điều này một cách chính xác, đây là cách tôi sẽ diễn đạt điều này từ góc độ của hàm triển khai IEnumerable với năng suất.

  • Đây là một.
  • Gọi lại nếu bạn cần.
  • Tôi sẽ nhớ những gì tôi đã cho bạn.
  • Tôi sẽ chỉ biết nếu tôi có thể cung cấp cho bạn một cái khác khi bạn gọi lại.

đơn giản và rực rỡ
Harry

10

Từ khóa năng suất C #, nói một cách đơn giản, cho phép nhiều cuộc gọi đến một bộ mã, được gọi là một trình vòng lặp, biết cách quay trở lại trước khi nó được thực hiện và khi được gọi lại, tiếp tục khi nó dừng lại - tức là nó giúp một trình vòng lặp trở thành trạng thái trong suốt cho mỗi mục theo trình tự mà trình vòng lặp trả về trong các cuộc gọi liên tiếp.

Trong JavaScript, khái niệm tương tự được gọi là Generators.


Giải thích tốt nhất chưa. Đây cũng là những máy phát điện tương tự trong python?
petrosmm

7

Đó là một cách rất đơn giản và dễ dàng để tạo ra vô số cho đối tượng của bạn. Trình biên dịch tạo một lớp bao bọc phương thức của bạn và thực hiện, trong trường hợp này, IEnumerable <object>. Nếu không có từ khóa suất, bạn phải tạo một đối tượng thực hiện <đối tượng> IEnumerable.


5

Nó tạo ra vô số trình tự. Những gì nó làm là thực sự tạo ra chuỗi IEnumerable cục bộ và trả về nó như là kết quả của phương thức


3

Đây liên kết có một ví dụ đơn giản

Các ví dụ đơn giản hơn ở đây

public static IEnumerable<int> testYieldb()
{
    for(int i=0;i<3;i++) yield return 4;
}

Lưu ý rằng lợi nhuận sẽ không trở lại từ phương thức. Bạn thậm chí có thể đặt WriteLinesauyield return

Ở trên tạo ra vô số 4 ints 4,4,4,4

Ở đây với một WriteLine . Sẽ thêm 4 vào danh sách, in abc, sau đó thêm 4 vào danh sách, sau đó hoàn thành phương thức và thực sự trả về từ phương thức (một khi phương thức đã hoàn thành, như sẽ xảy ra với một thủ tục mà không trả về). Nhưng điều này sẽ có một giá trị, một IEnumerabledanh sách ints, mà nó trả về khi hoàn thành.

public static IEnumerable<int> testYieldb()
{
    yield return 4;
    console.WriteLine("abc");
    yield return 4;
}

Cũng lưu ý rằng khi bạn sử dụng năng suất, những gì bạn đang trả về không cùng loại với hàm. Đó là loại của một yếu tố trong IEnumerabledanh sách.

Bạn sử dụng năng suất với kiểu trả về của phương thức là IEnumerable. Nếu kiểu trả về của phương thức là inthoặcList<int> và bạn sử dụng yield, thì nó sẽ không biên dịch. Bạn có thể sử dụng IEnumerablekiểu trả về mà không có năng suất nhưng có vẻ như bạn không thể sử dụng năng suất mà không cóIEnumerable loại trả về phương thức.

Và để thực hiện nó, bạn phải gọi nó theo một cách đặc biệt.

static void Main(string[] args)
{
    testA();
    Console.Write("try again. the above won't execute any of the function!\n");

    foreach (var x in testA()) { }


    Console.ReadLine();
}



// static List<int> testA()
static IEnumerable<int> testA()
{
    Console.WriteLine("asdfa");
    yield return 1;
    Console.WriteLine("asdf");
}

lưu ý- nếu cố gắng hiểu SelectMany, nó sử dụng năng suất và cả public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { yield return t; }public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { return new List<TResult>(); }
tổng quát

Hình như giải thích rất tốt! Đây có thể là câu trả lời được chấp nhận.
pongapundit

@pongapundit cảm ơn, câu trả lời của tôi chắc chắn rất rõ ràng và đơn giản, nhưng tôi chưa sử dụng năng suất nhiều, những người trả lời khác có nhiều kinh nghiệm với nó và kiến ​​thức về cách sử dụng của nó hơn tôi. Những gì tôi đã viết về năng suất ở đây có lẽ là từ việc gãi đầu cố gắng tìm ra một số câu trả lời ở đây và tại liên kết dotnetperls đó! Nhưng vì tôi không biết rõ yield returnđiều đó (ngoài những điều đơn giản mà tôi đã đề cập), và tôi đã không sử dụng nó nhiều và không biết nhiều về cách sử dụng của nó, tôi không nghĩ rằng đây nên là thứ được chấp nhận.
barlop

3

Một điểm chính về từ khóa Yield là Lazy Ex thi . Bây giờ điều tôi muốn nói với Lazy Ex thi là thực thi khi cần. Một cách tốt hơn để đặt nó là bằng cách đưa ra một ví dụ

Ví dụ: Không sử dụng Yield tức là không thực hiện lệnh lười biếng.

        public static IEnumerable<int> CreateCollectionWithList()
        {
            var list =  new List<int>();
            list.Add(10);
            list.Add(0);
            list.Add(1);
            list.Add(2);
            list.Add(20);

            return list;
        }

Ví dụ: sử dụng Yield tức là thực thi lười biếng.

    public static IEnumerable<int> CreateCollectionWithYield()
    {
        yield return 10;
        for (int i = 0; i < 3; i++) 
        {
            yield return i;
        }

        yield return 20;
    }

Bây giờ khi tôi gọi cả hai phương thức.

var listItems = CreateCollectionWithList();
var yieldedItems = CreateCollectionWithYield();

bạn sẽ thấy listItems sẽ có 5 mục bên trong nó (di chuột vào listItems trong khi gỡ lỗi). Trong khi đóieldItems sẽ chỉ có một tham chiếu đến phương thức chứ không phải các mục. Điều đó có nghĩa là nó đã không thực hiện quá trình nhận các mục bên trong phương thức. Một cách rất hiệu quả để có được dữ liệu chỉ khi cần thiết. Việc triển khai năng suất thực tế có thể thấy trong ORM như Entity Framework và NHibernate, v.v.


-3

Đó là cố gắng mang lại một số tính tốt của Ruby :)
Khái niệm: Đây là một số Mã Ruby mẫu in ra từng phần tử của mảng

 rubyArray = [1,2,3,4,5,6,7,8,9,10]
    rubyArray.each{|x| 
        puts x   # do whatever with x
    }

Mỗi triển khai phương thức của Array mang lại quyền kiểm soát cho người gọi ('đặt x') với mỗi phần tử của mảng được trình bày gọn gàng dưới dạng x. Người gọi sau đó có thể làm bất cứ điều gì cần làm với x.

Tuy nhiên .Net không đi hết con đường ở đây .. C # dường như đã kết hợp năng suất với IEnumerable, theo cách buộc bạn phải viết một vòng lặp foreach trong trình gọi như đã thấy trong phản hồi của Mendelt. Ít thanh lịch.

//calling code
foreach(int i in obCustomClass.Each())
{
    Console.WriteLine(i.ToString());
}

// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
   for(int iLooper=0; iLooper<data.Length; ++iLooper)
        yield return data[iLooper]; 
}

7
-1 Câu trả lời này không đúng với tôi. Có, C # yieldđược kết hợp với IEnumerablevà C # thiếu khái niệm Ruby về "khối". Nhưng C # có lambdas, có thể cho phép thực hiện một ForEachphương thức, rất giống với Ruby each. Điều đó không có nghĩa là nó sẽ là một ý tưởng tốt để làm như vậy , mặc dù.
rsenna

Tốt hơn nữa: công khai IEn Countable <int> Each () {int index = 0; năng suất trả về dữ liệu [index ++]; }
ata
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.