2015-10-05 18 views
10

Chúng tôi đang cố gắng sử dụng IEnumerable làm nhà máy tạo ra các đối tượng khác nhau mỗi khi chúng ta lặp lại nó. Những người cần được GC'ed càng sớm càng tốt. Tuy nhiên, lưu ý rằng chúng tôi giữ một tham chiếu đến điều tra số để chúng tôi có thể gọi lại. Vì vậy, chương trình của chúng tôi về cơ bản trông như thế này:Khi sử dụng "lợi nhuận" tại sao loại trình biên dịch tạo ra cả IEnumerable và IEnumerator

public class YieldSpec 
{ 
    public static IEnumerable<string> Strings() 
    { 
     yield return "AAA"; 
     yield return "BBB"; 
     yield return "CCC"; 
    } 
    public void YieldShouldAllowGC() 
    { 
     var e = Strings(); 
     foreach (var a in e) 
     { 
      Console.WriteLine(a); 
     } 

    } 
} 

Nhìn vào đó mã trong trình gỡ lỗi:

enter image description here

Bạn có thể thấy rằng khi breakpoint là hit các IEnumerable có một tham chiếu đến "CCC ".

Điều này không thực sự xảy ra. IEnumerable chỉ nên tạo một IEnumerator khi GetEnumerator được gọi. Đây có phải là hành vi mong đợi mà IEnumerable có thể chứa trạng thái không?

+0

Tôi không thể tạo lại. Chắc chắn bạn đã xử lý tất cả các trường hợp? Sử dụng [mã này] (https://dotnetfiddle.net/M4AdN8). –

+0

Bạn đã loại bỏ cá thể thành 'Kiểm tra'? Nó không vứt bỏ toàn bộ thứ cho đến khi bạn vứt bỏ 'Test'. (nó là lười biếng) –

+1

AFAIK phạm vi để lại 'Test()' (hoặc cụ thể hơn, sau khi 'foreach()' đã) sẽ làm cho điều tra viên đủ điều kiện thu gom rác thải. Vấn đề là gì? Đó là _directly_ sau khi gọi 'Test()', GC chưa khởi động? – CodeCaster

Trả lời

8

Là một chi tiết triển khai vì lý do hiệu suất, có, máy trạng thái thực hiện cả hai IEnumerableIEnumerator. Điều đó nói rằng, nó là đủ thông minh để làm điều này một cách chính xác. Đây chỉ là lần đầu tiên mà IEnumerable được yêu cầu cho một số IEnumerator mà nó tự trả về. Bất kỳ cuộc gọi nào trong tương lai tới GetEnumerator sẽ dẫn đến một cá thể mới của đối tượng đang được tạo, để các trạng thái của trình lặp riêng biệt có thể được duy trì. Điều này được thực hiện bởi vì trong khi điều quan trọng là có thể để tạo một IEnumerable tạo nhiều IEnumerators, thì lớn nhất phần lớn các tình huống thực tế liên quan đến chính xác một trường hợp được tạo ra, vì vậy đó là tình huống được tối ưu hóa.

+0

Điều đó làm rõ một số nhầm lẫn mà tôi đã thấy khi chạy trình gỡ rối. Tôi đã chạy IEnumerable nhiều lần nhưng chỉ thấy thuộc tính __Current__ đang được cập nhật trong lần chạy đầu tiên. Điều này làm cho nó tất cả rõ ràng. – bradgonesurfing

+2

@bradgonesurfing: Để làm rõ câu trả lời đúng này, "lý do hiệu suất" ở đây là * để giảm áp lực GC *. Bạn có thể dễ dàng tưởng tượng một tình huống mà những phương pháp này có chứa 'yield's được gọi là hàng ngàn lần một giây và ngay lập tức liệt kê. Nếu chúng ta có thể giảm một nửa số lượng vật thể mới được tạo ra khi điều đó xảy ra, đó là một chiến thắng rõ ràng. –

+1

Xin chào @EricLippert Tôi hoàn toàn hiểu được lý do và có vẻ như một sự cân bằng hoàn hảo. Nó chỉ vấp ngã tôi bởi vì tôi tìm thấy một tải toàn bộ các đối tượng thông qua hồ sơ mà tôi giả định nên đã được GC'd. Trong trường hợp của tôi, nó là nút gốc của một hệ thống heirarchy lớn của các menu ngữ cảnh WPF. Tôi muốn họ nhận được GC'd thay vì được tổ chức bởi IEnumerable. Trường hợp của tôi là một ngoại lệ và tôi sẽ tìm thấy một cách xung quanh nó. – bradgonesurfing

Các vấn đề liên quan