2009-12-28 11 views
13

Làm việc thông qua một hướng dẫn (Professional ASP.NET MVC - Nerd Dinner), tôi tình cờ gặp đoạn mã này:sử dụng Thú vị của C# suất từ ​​khóa trong Nerd Dinner hướng dẫn

public IEnumerable<RuleViolation> GetRuleViolations() { 
    if (String.IsNullOrEmpty(Title)) 
     yield return new RuleViolation("Title required", "Title"); 
    if (String.IsNullOrEmpty(Description)) 
     yield return new RuleViolation("Description required","Description"); 
    if (String.IsNullOrEmpty(HostedBy)) 
     yield return new RuleViolation("HostedBy required", "HostedBy"); 
    if (String.IsNullOrEmpty(Address)) 
     yield return new RuleViolation("Address required", "Address"); 
    if (String.IsNullOrEmpty(Country)) 
     yield return new RuleViolation("Country required", "Country"); 
    if (String.IsNullOrEmpty(ContactPhone)) 
     yield return new RuleViolation("Phone# required", "ContactPhone"); 
    if (!PhoneValidator.IsValidNumber(ContactPhone, Country)) 
     yield return new RuleViolation("Phone# does not match country", "ContactPhone"); 
    yield break; 
} 

Tôi đã đọc lên trên yield, nhưng tôi đoán sự hiểu biết của tôi vẫn còn hơi mờ. Điều có vẻ như là tạo một đối tượng mà cho phép đi xe đạp qua các mục trong bộ sưu tập mà không thực sự thực hiện thao tác đi xe đạp trừ khi và cho đến khi cần thiết.

Ví dụ này hơi lạ với tôi. Những gì tôi nghĩ nó đang làm là trì hoãn việc tạo ra bất kỳ trường hợp RuleViolation nào cho đến khi lập trình viên thực sự yêu cầu một mục cụ thể trong bộ sưu tập bằng cách sử dụng for each hoặc phương pháp mở rộng LINQ như .ElementAt(2).

Ngoài này, tuy nhiên, tôi có một số câu hỏi sau:

  1. Khi nào những phần điều kiện của if báo cáo nhận được đánh giá? Khi GetRuleViolations() được gọi hoặc khi số đếm thực sự được lặp lại? Nói cách khác, nếu giá trị của Title thay đổi từ null đến Really Geeky Dinner giữa thời gian tôi gọi GetRuleViolations() và thời gian tôi cố gắng thực sự lặp lại, thì RuleViolation("Title required", "Title") có được tạo hay không?

  2. Tại sao cần yield break;? Nó thực sự làm gì ở đây?

  3. Giả sử Title là rỗng hoặc trống. Nếu tôi gọi GetRuleViolations() sau đó lặp lại kết quả hai lần trong một hàng, số lần new RuleViolation("Title required", "Title") sẽ được gọi là bao nhiêu?

+3

Trình biên dịch .Net biến đường cú pháp này thành dạng bastardized hơn. Biên dịch mẫu và sau đó tải IL trong phản xạ. Bạn nên được abe để hiểu chính xác những gì đang xảy ra ở đó. –

Trả lời

17

Một chức năng có chứa yield lệnh được xử lý khác biệt so với một chức năng bình thường. Điều gì đang xảy ra đằng sau hậu trường khi hàm đó được gọi, là một kiểu ẩn danh được xây dựng theo loại hàm cụ thể IEnumerable, hàm tạo một đối tượng kiểu đó và trả về nó. Lớp ẩn danh chứa logic thực hiện phần thân của hàm cho đến khi lệnh yield tiếp theo cho mỗi lần gọi là IEnumerable.MoveNext. Nó là một chút gây hiểu nhầm, phần thân của hàm không được thực hiện trong một lô giống như hàm bình thường, mà đúng hơn là từng phần, mỗi phần thực hiện khi điều tra viên di chuyển một bước về phía trước.

Liên quan đến câu hỏi của bạn:

  1. Như tôi đã nói, mỗi if được thực hiện khi bạn lặp tới phần tử tiếp theo.
  2. yield break thực sự không cần thiết trong ví dụ trên. Những gì nó làm là nó chấm dứt điều tra.
  3. Mỗi khi bạn lặp lại số đếm, bạn buộc thực thi lại mã. Đặt điểm ngắt trên đường liên quan và tự kiểm tra.
+0

+1 câu trả lời thực sự rõ ràng và hữu ích, cảm ơn. Chỉ cần một câu hỏi tiếp theo nhỏ nếu bạn không nhớ: Bạn có thể nghĩ ra một ví dụ mà 'ngắt kết quả' * là cần thiết không? – devuxer

+1

Làm rõ tiểu sử - nghĩa là "IEnumerator []' ở một vài nơi mà bạn nói 'IEnumerable []'. –

+0

Vì vậy, trong ví dụ, 'ngắt sản lượng' là cần thiết, bởi vì nếu không có' RuleViolation' được tạo ra, nó sẽ ném một ngoại lệ, phải không? – Ronald

6

1) Lấy ví dụ đơn giản này:

public void Enumerate() 
{ 
    foreach (var item in EnumerateItems()) 
    { 
     Console.WriteLine(item); 
    } 
} 

public IEnumerable<string> EnumerateItems() 
{ 
    yield return "item1"; 
    yield return "item2"; 
    yield break; 
} 

Mỗi lần bạn gọi MoveNext() từ IEnumerator trở về mã từ các yield điểm và di chuyển đến dòng thực thi tiếp theo của mã.

2) yield break; sẽ cho biết IEnumerator rằng không có gì để liệt kê.

3) một lần cho mỗi điều tra.

Sử dụng yield break;

public IEnumerable<string> EnumerateUntilEmpty() 
{ 
    foreach (var name in nameList) 
    { 
     if (String.IsNullOrEmpty(name)) yield break; 
     yield return name; 
    }  
} 
+0

Chắc chắn về "3) một lần"? Theo tôi hiểu, điều này được đánh giá lại mỗi lần (xem hai câu trả lời khác ở đây). –

+0

Tôi đã làm rõ. – ChaosPandion

+0

Nếu bạn tạo một danh sách, tại sao không lặp lại nó? Cách 'yield' cho phép bạn không xây dựng danh sách một cách rõ ràng. Trong trường hợp của bạn, ví dụ: công khai IEnumerable Liệt kê() { trả lại lợi nhuận "item1"; lợi nhuận trả về "item2"; } –

2

phiên bản ngắn:

1: Lợi tức là sự kỳ diệu "Stop và quay lại sau" từ khóa, do đó nếu phát biểu ở phía trước của "hoạt động" một đã được đánh giá.

2: ngắt kết quả rõ ràng kết thúc điều tra (nghĩ "ngắt" trong trường hợp chuyển đổi)

3: Mọi lúc. Bạn có thể cache kết quả, tất nhiên, bằng cách chuyển nó thành một List chẳng hạn và lặp lại nó sau đó.