2010-03-10 10 views
38

Hãy xem xét bit này của mã bị xáo trộn. Mục đích là tạo một đối tượng mới khi đang chạy qua hàm tạo ẩn danh và yield return nó. Mục đích là để tránh phải duy trì một bộ sưu tập địa phương chỉ đơn giản là return nó.C#: lợi tức lợi nhuận trong vòng foreach thất bại - nội dung không thể là khối lặp lại

public static List<DesktopComputer> BuildComputerAssets() 
{   
    List<string> idTags = GetComputerIdTags(); 

    foreach (var pcTag in idTags) 
    { 
     yield return new DesktopComputer() {AssetTag= pcTag 
              , Description = "PC " + pcTag 
              , AcquireDate = DateTime.Now 
              }; 
    }    
} 

Thật không may, chút mã này tạo ra một ngoại lệ:

Lỗi 28 Phần nội dung của 'Foo.BuildComputerAssets()' không thể là một khối iterator vì 'System.Collections.Generic.List' không phải là một loại giao diện iterator

Câu hỏi

  • Thông báo lỗi này có ý nghĩa gì?
  • Làm cách nào để tránh lỗi này và sử dụng yield return đúng cách?

Trả lời

49

Bạn chỉ có thể sử dụng yield return trong một hàm trả về một IEnumerable hoặc một IEnumerator, không phải là một List<T>.

Bạn cần thay đổi chức năng của mình để trả lại IEnumerable<DesktopComputer>.

Ngoài ra, bạn có thể viết lại các chức năng để sử dụng List<T>.ConvertAll:

return GetComputerIdTags().ConvertAll(pcTag => 
    new DesktopComputer() { 
     AssetTag = pcTag, 
     Description = "PC " + pcTag, 
     AcquireDate = DateTime.Now 
    }); 
16

phương pháp chữ ký của bạn là sai. Nó nên là:

public static IEnumerable<DesktopComputer> BuildComputerAssets() 
8

yield chỉ hoạt động trên các loại Iterator:

Tuyên bố năng suất chỉ có thể xuất hiện bên trong một khối iterator

Iterators được định nghĩa là

Kiểu trả về của một trình lặp phải là IEnumerable, IEnumerator, IEnume rable <T> hoặc IEnumerator <T>.

IList và IList <T> làm thực hiện IEnumerable/IEnumerable <T>, nhưng mỗi người gọi đến một Enumerator hy vọng một trong bốn loại trên và không có gì khác.

2

Bạn cũng có thể triển khai cùng chức năng bằng truy vấn LINQ (trong C# 3.0+). Điều này kém hiệu quả hơn so với phương pháp ConvertAll, nhưng tổng quát hơn. Sau đó, bạn cũng có thể cần phải sử dụng các tính năng khác như LINQ lọc:

return (from pcTag in GetComputerIdTags() 
     select new DesktopComputer() { 
      AssetTag = pcTag, 
      Description = "PC " + pcTag, 
      AcquireDate = DateTime.Now 
     }).ToList(); 

Phương pháp ToList chuyển đổi kết quả IEnumerable<T>-List<T>.Cá nhân tôi không thích ConvertAll, bởi vì nó cũng giống như LINQ. Nhưng bởi vì nó đã được thêm vào trước đó, nó không thể được sử dụng với LINQ (nó nên được gọi là Select).