2010-08-26 31 views

Trả lời

16

yield return là một nhà điều hành khá tiện dụng cho điều này, mặc dù nó không thực sự yêu cầu LINQ cụ thể.

IEnumerable<int> GetInfiniteSeries(IEnumerable<int> items) { 
    while (true) { 
     foreach (var item in items) { 
      yield return item; 
     } 
    } 
} 
+1

ngắn hơn rất nhiều so với phiên bản của tôi :) –

6
IEnumerable<T> Infinite(this IEnumerable<T> ienum) 
{ 
    List<T> list = ienum.ToList(); 
    while (true) 
     foreach(var t in list) 
      yield return t; 
} 



foreach(int i in Enumerable.Range(1,3).Infinite()) 
     Console.WriteLine(i); 
+0

là cuộc gọi cho 'ToList()' cần thiết? –

+0

+1: 2 vấn đề mặc dù: 1. Điều này sẽ không hoạt động với 'ienum.Infinite(). Infinite()', khi một cách hợp lý việc thực thi 'Infinite()' sẽ hỗ trợ điều này. 2. Nếu chúng ta bỏ bê điểm 1, có một vấn đề về hiệu suất: điều tra viên tiếp tục được tái tạo và xử lý. Nó sẽ được tốt hơn nhiều nó đã được viết lại như là một vòng lặp mà resets để '0' khi nó hits' list.Count'. Cách khác là dựa vào 'IEnumerator.Reset()', nhưng tôi cho rằng nó là nguy hiểm vì quá nhiều triển khai không hỗ trợ nó. – Ani

+2

@Ani: Làm thế nào để bạn biết có vấn đề về hiệu năng? Bạn có biết rằng không có bằng chứng thực nghiệm rằng việc tạo và phá hủy một trình vòng lặp danh sách - một cấu trúc được thiết kế đặc biệt bởi nhóm BCL sẽ cực kỳ nhanh chóng để phân bổ và xử lý - là điều * chậm nhất * trong ứng dụng của người dùng? Hoặc bạn đã làm công việc định hình cẩn thận rộng rãi để xác định rằng phân bổ và xử lý của cấu trúc này là trong thực tế vấn đề hiệu suất lớn nhất trong ứng dụng của người dùng? Nếu vậy thì tôi muốn thấy dữ liệu đó để tôi có thể chuyển nó cho nhóm thực hiện BCL, cảm ơn! –

3

Dưới đây là cách tôi đã thực hiện nó cuối cùng:

public static IEnumerable<T> AdNauseam<T>(this IEnumerable<T> i_list) 
    { 
     using(var etor = i_list.GetEnumerator()) 
     { 
      while(true) 
      { 
       while(etor.MoveNext()) 
       { 
        yield return etor.Current; 
       } 
       etor.Reset(); 
      } 
     } 
    } 

Cách sử dụng:

var list = new[] {1, 2, 3} 
var infinite = list.AdNauseam().Take(10); 

Kết quả:

{1, 2, 3, 1, 2, 3, 1, 2, 3, 1} 
+0

Tôi đang tự hỏi nếu việc sử dụng() là hữu ích trong trường hợp này. –

+1

Việc sử dụng() là cần thiết - IEnumerator triển khai IDisposable. Đối với hầu hết các danh sách, việc vứt bỏ có thể không làm được nhiều, nhưng nếu điều tra viên đang làm một cái gì đó mới lạ như đọc từ một tập tin hoặc một cơ sở dữ liệu, bạn sẽ muốn nó được xử lý. –

+2

Tôi tự hỏi liệu việc sử dụng Reset có phải là lựa chọn tốt nhất cho rằng 'Phương pháp Reset được cung cấp cho khả năng tương tác COM. Nó không nhất thiết cần phải được thực hiện; thay vào đó, người triển khai có thể chỉ cần ném một NotSupportedException. ' (nguồn: http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.reset(v=vs.110).aspx) –

2

Một lựa chọn khác, thực hiện IEnumerator<T>:

public class InfiniteEnumerator<T> : IEnumerator<T> 
    { 
     private IList<T> _items; 
     private int _index = -1; 

     public InfiniteEnumerator(IList<T> items) 
     { 
      if (items == null) 
      { 
       throw new ArgumentNullException("items"); 
      } 
      _items = items; 
     } 

     public T Current 
     { 
      get { return _items[_index]; } 
     } 

     public void Dispose() 
     { 

     } 

     object System.Collections.IEnumerator.Current 
     { 
      get { return _items[_index]; } 
     } 

     public bool MoveNext() 
     { 
      if (_items.Count == 0) 
      { 
       return false; 
      } 

      _index = (_index + 1) % _items.Count; 
      return true; 
     } 

     public void Reset() 
     { 
      _index = -1; 
     } 
    } 
+1

Tôi thích triển khai này vì nó mô tả chi tiết hơn về những gì bạn đang thực sự làm: liệt kê danh sách vô thời hạn. Nó cảm thấy đẹp hơn nhiều so với ý tưởng về một IEnumerable vô hạn, và như Ani đã đề cập, tránh bộ phát hiện não là 'ienum.Infinite(). Infinite()' – batwad

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