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?
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;
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?
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;
Câu trả lời:
Nó chỉ định rằng một trình vòng lặp đã kết thúc. Bạn có thể nghĩ về yield break
một return
tuyê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.
break
thay vì yield break
trong ví dụ của bạn ở trên? Trình biên dịch không phàn nàn về điều đó.
break
trong 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.
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).
yield return x
cả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 return
câ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.
Kết thúc một khối lặp (ví dụ: không có thêm phần tử nào trong IEnumerable).
yield
từ 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
yield
phả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ả IEnumerable
khô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.
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;
}
}
yield
về 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 return
giố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 yield
là, 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.
Toàn bộ chủ đề của các khối lặp được trình bày rõ trong chương mẫu miễn phí này từ cuốn sách C # in Depth của Jon Skeet .
Các yield break
tuyên bố gây ra liệt kê để dừng lại. Trong thực tế, yield break
hoà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 maxCount
số 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 break
công trình khác nhau hơn chỉ là một đồng bằng break
. Trong ví dụ trên, yield break
thoát khỏi phương thức mà không thực hiện cuộc gọi đến Debug.WriteLine(..)
.
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 break
câ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.
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;
}
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.
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.
yield break
không
yield return
thự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ó.
yield break
là 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 break
cung 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ẻ