2011-01-21 28 views
9

Tôi có văn bản về tùy chỉnh IEnumerator. Whats cách đơn giản nhất để thực hiện IEnumerable từ nó là gì? Giải pháp lý tưởng (một dòng mã) sẽ là nếu có một số lớp cho mục đích đó. Hay tôi phải tự tạo ra?IEnumerable từ IEnumerator

+1

Thật không may là bạn phải tự tạo. Cách tiếp cận bảo thủ nhất là dành cho bạn để lưu trữ tất cả các mục bên trong enumerable mới của bạn, để xử lý các điều tra viên chỉ có thể liệt kê một lần, vì vậy không có lớp chung cho điều này trong thời gian chạy. Tại sao, cụ thể, bạn có cần thực hiện việc này không? Tại sao bạn không thể làm việc với bản gốc có thể đếm được trong cài đặt này? –

+0

@Lasse V. Karlsen: Lý do hiệu suất. Tôi cần một 'IEnumerable' của tất cả trẻ em 'DependencyObject' của một số UIElement. Để có được chúng, tôi sử dụng lớp 'VisualTreeHelper'. Nhưng tôi phải xem xét rằng có thể có rất nhiều trẻ em để sao chép vào một số mảng hoặc Bộ sưu tập sẽ tốn kém. - drasto 5 phút trước – drasto

+1

Ngoài ra - câu hỏi ngẫu nhiên ... ** tại sao ** có bạn viết tay một 'IEnumerator' - tại sao không chỉ là một khối lặp? mà btw có thể được (tự động) 'IEnumerable []' ** hoặc ** 'IEnumerator []'. Khác với các ví dụ để so sánh, tôi đã không viết một trình vòng lặp theo cách thủ công vì C# 2.0 –

Trả lời

4

Tôi thực sự sẽ tiếp cận điều này theo cách khác; trong khi bạn có thể (theo câu trả lời tuyệt vời của Mike P), hãy liệt kê một số điều mà bạn không thể thực hiện được - ví dụ: hy vọng (mặc dù, công bằng chứ không phải khăng khăng) rằng bạn có thể nhận được nhiều điều tra viên từ một số, lý tưởng bị cô lập và lặp lại. Vì vậy, nếu tôi làm:

Assert.AreEqual(sequence.Sum(), sequence.Sum()); 

nhưng nếu bạn "giả mạo" điều tra vào một liệt kê, chuỗi thứ hai sẽ trống. Hoặc nếu bạn làm chúng song song - chỉ kỳ lạ thôi. Và có phương pháp xử lý chúng song song - xem xét:

Assert.IsTrue(sequence.SequenceEqual(sequence)); 

làm việc này cả hai điều tra viên tiến cùng lúc, vì vậy nếu bạn chỉ một Enumerator, bạn đang khá tiêu tan.

đặt lại trên bảng liệt kê, nhưng điều này phần lớn là lỗi thiết kế và không được sử dụng (thậm chí là yêu cầu chính thức trong thông số rằng khối lặp sẽ ném ngoại lệ nếu bạn gọi).

Câu hỏi (IMO) tốt hơn là "làm cách nào để nhận điều tra từ một số", trong trường hợp đó câu trả lời là "gọi GetEnumerator(), và nhớ kiểm tra để vứt bỏ vòng lặp" - hoặc theo thuật ngữ đơn giản hơn " sử dụng foreach ".

+0

Sai lầm với "Đặt lại" là chỉ có một loại đếm được. IMHO, phải có một IMultipassEnumerable, kế thừa IEnumerable, mà sẽ hỗ trợ Reset và đảm bảo rằng nhiều pass sẽ trả về dữ liệu giống hệt nhau hoặc ném một ngoại lệ; một IEnumerable bình thường có bộ sưu tập đã được sửa đổi nên được phép trả lại dữ liệu 'hợp lý' nếu nó có thể làm như vậy hoặc ném một ngoại lệ nếu nó không thể, và một ISafeEnumerable, mà sẽ được dự kiến ​​sẽ làm việc một cách hợp lý (không ném một ngoại lệ) thậm chí nếu một bộ sưu tập thay đổi. Một chút muộn để thay đổi mọi thứ, mặc dù. – supercat

13

Không có phương pháp tích hợp sẵn. Tôi có phương pháp này mở rộng mà tôi sử dụng thường xuyên đủ:

static IEnumerable Iterate(this IEnumerator iterator) 
{ 
while (iterator.MoveNext()) 
    yield return iterator.Current; 
} 
+0

+1, nhưng cảnh báo: Phương thức 'Reset()' sẽ không hoạt động theo cách này. – Mehrdad

+0

Xem câu trả lời của tôi cho một vài điều khác sẽ không hoạt động theo cách này –

2

Khá đơn giản:

class Enumerate : IEnumerable 
{ 
    private Enumerate IEnumerator it; 
    public Enumerate(IEnumerator it) { this.it = it; } 
    public IEnumerator GetEnumerator() { return this.it; } 
} 

này cũng cho phép người dùng gọi IEnumerator.Reset() nếu Enumerator bạn đã cho nó hỗ trợ nó.

+0

tôi cũng đã sử dụng điều này và đã rất vui. nhưng điều này vi phạm ngữ nghĩa IEnumerable và thời gian đến khi tôi nhận được kết quả sai. Vì vậy, chuyển sang trật tự tạo ra một phiên bản lặp mới (xem câu trả lời của tôi) – citykid

+0

@citykid: Vâng, tiền đề của việc này ở nơi đầu tiên là vấn đề ... nhưng nếu bạn phải, đây là cách bạn làm điều đó. – Mehrdad

4

Trong bộ sưu tập của tôi về C# utils Tôi có điều này:

class Enumerable<T> : IEnumerable<T> 
{ 
    Func<IEnumerator<T>> factory; 
    public Enumerable(Func<IEnumerator<T>> factory) { this.factory = factory; } 
    public IEnumerator<T> GetEnumerator() { return this.factory(); } 
    IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } 
} 

này có một IEnumerator nhà máy chức năng, mà thường có thể được cung cấp rất dễ dàng thay vì dụ IEnumerator đơn (trong đó sản lượng kết quả sai sau đầu tiên lặp lại và phá vỡ ngữ nghĩa của IEnumerable). Điều này tránh được các vấn đề được Marc Gravell đánh dấu và thiết lập đầy đủ hành vi IEnumerable.

tôi sử dụng nó theo cách này:

IEnumerable<Fruit> GetFruits() 
{ 
    var arg1 = ... 
    return new Enumerable<Fruit>(() => new FruitIterator(arg1, arg2, ...)); 
} 
+3

Đây là một giải pháp rất đẹp –

0

Những gì tôi làm là tạo một lớp mà thực hiện cả hai IEnumerator và IEnumerable. Làm cho GetEnumerator() trả về chính nó và bạn có thể lặp lại nó như bình thường.

public class MyClassEnumerator : IEnumerator<MyClass>, IEnumerable<MyClass> 
{ 
    public MyClass Current { get; private set; } 

    object IEnumerator.Current => Current; 

    public void Reset() 
    { 
     ... 
    } 

    public bool MoveNext() 
    { 
     ... 
    } 

    public IEnumerator<MyClass> GetEnumerator() 
    { 
     return this; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this; 
    } 

    public void Dispose() 
    { 
     ... 
    } 
} 
Các vấn đề liên quan