2011-02-10 29 views
8

Tôi đang viết một trình bao bọc xung quanh một thư viện của bên thứ ba và nó có một phương pháp để quét dữ liệu mà nó quản lý. Phương thức này có một phương thức gọi lại mà nó gọi cho mỗi mục trong dữ liệu mà nó tìm thấy.Có thể chuyển các cuộc gọi lại thành IEnumerable

ví dụ: Phương pháp này về cơ bản là: void Scan(Action<object> callback);

Tôi muốn quấn nó và phơi bày một phương pháp như IEnumerable<object> Scan();

Đây có phải là có thể mà không cần đến một chủ đề riêng biệt để thực hiện quét thực tế và một bộ đệm?

+0

Bạn có thể phác thảo luồng chính xác trên thư viện của bên thứ 3 của bạn - có vẻ như nó đã sinh ra nhiều chuỗi. Cũng nên nhớ rằng bạn có thể có một đại biểu Multicast. Ngoài ra, làm cách nào để bạn xác định rằng quá trình quét đã kết thúc? – weismat

+0

Thành thật mà nói, tôi không thể thấy giá trị khi thực hiện việc này. Nếu bạn muốn làm 'foreach (object obj trong scannable.Scan()) {/ * làm điều gì đó với obj * /}', có thực sự tốt hơn 'scannable.Scan (obj => {/ * làm điều gì đó với obj * /}); '? – Flynn1179

+0

@ weismat: không, các chủ đề không sinh sản của nó, về cơ bản nó lặp lại với lưu trữ dữ liệu của nó trong nội bộ và gọi hàm gọi lại cho mỗi bản ghi. – Gareth

Trả lời

4

Bạn có thể làm điều này khá đơn giản với phản ứng:

class Program 
{ 
    static void Main(string[] args) 
    { 
     foreach (var x in CallBackToEnumerable<int>(Scan)) 
      Console.WriteLine(x); 
    } 

    static IEnumerable<T> CallBackToEnumerable<T>(Action<Action<T>> functionReceivingCallback) 
    { 
     return Observable.Create<T>(o => 
     { 
      // Schedule this onto another thread, otherwise it will block: 
      Scheduler.Later.Schedule(() => 
      { 
       functionReceivingCallback(o.OnNext); 
       o.OnCompleted(); 
      }); 

      return() => { }; 
     }).ToEnumerable(); 
    } 

    public static void Scan(Action<int> act) 
    { 
     for (int i = 0; i < 100; i++) 
     { 
      // Delay to prove this is working asynchronously. 
      Thread.Sleep(100); 
      act(i); 
     } 
    } 
} 

Hãy nhớ rằng đây không chăm sóc những thứ như hủy bỏ, vì phương pháp gọi lại không thực sự cho phép nó. Một giải pháp thích hợp sẽ yêu cầu làm việc trên một phần của thư viện bên ngoài.

+0

Hmmm, sẽ hoạt động tốt.Tôi hy vọng tránh phải sử dụng một sợi riêng biệt, nhưng nó dường như không thể sử dụng chỉ có một sợi. Điều này có vẻ là cách đơn giản nhất để làm điều đó. Cảm ơn. – Gareth

+0

Vâng tôi không nghĩ rằng nó thực sự có thể để có được nó vào một Enumerable sử dụng chỉ có một sợi. Gần nhất tôi có thể nhận được là một Observable, nhưng trong trường hợp đó nó chỉ là một cuộc gọi lại bằng tên khác :) – porges

-1

Hãy xem từ khóa yield - từ khóa này sẽ cho phép bạn có một phương thức trông giống như IEnumerable nhưng thực tế không xử lý cho từng giá trị trả về.

+1

Vâng, nhưng tôi nghĩ rằng OP muốn biết làm thế nào để móc như vậy một phương thức cho một cuộc gọi lại 'Action '. – LukeH

+0

Ồ, tôi hiểu ý của bạn là gì - câu hỏi khó hơn nhiều! –

+0

Vâng, suy nghĩ ban đầu của tôi là một phương pháp một dòng 'Scan (x => {yield return x;}); ', nhưng tiếc là yield không hoạt động như thế. – Flynn1179

0

Làm thế nào về vấn đề này một:

IEnumerable<Object> Scan() 
{ 
    List<Object> objList = new List<Object>(); 

    Action<Object> action = (obj) => { objList.Add(obj); }; 

    Scan(action); 

    return objList; 
} 
+1

'Quét (hành động)' có thể thêm các mục trong khi trả về 'IEnumerable' sẽ được lặp lại dẫn đến một ngoại lệ kể từ khi gọi lại sẽ thực thi async. – Cornelius

+0

Yea, điều đó có hiệu quả, nhưng số lượng callback tiềm năng có thể là hàng triệu, vì vậy tôi không muốn đệm toàn bộ quá trình trước khi lặp lại nó. – Gareth

4

Bạn nên điều tra các Rx project - điều này cho phép một nguồn sự kiện để được tiêu thụ như một IEnumerable.

Tôi không chắc liệu nó có cho phép gọi lại vani như vậy (nó nhắm vào các sự kiện .NET) nhưng nó sẽ đáng để xem xét khi có thể gọi lại một cách bình thường như là IObservable.

2

Đây là một Enumerator chặn (phương pháp Scan cần phải chạy trong một thread riêng biệt)

public class MyEnumerator : IEnumerator<object> 
    { 
     private readonly Queue<object> _queue = new Queue<object>(); 
     private ManualResetEvent _event = new ManualResetEvent(false); 

     public void Callback(object value) 
     { 
      lock (_queue) 
      { 
       _queue.Enqueue(value); 
       _event.Set(); 
      } 
     } 

     public void Dispose() 
     { 

     } 

     public bool MoveNext() 
     { 
      _event.WaitOne(); 
      lock (_queue) 
      { 
       Current = _queue.Dequeue(); 
       if (_queue.Count == 0) 
        _event.Reset(); 
      } 
      return true; 
     } 

     public void Reset() 
     { 
      _queue.Clear(); 
     } 

     public object Current { get; private set; } 

     object IEnumerator.Current 
     { 
      get { return Current; } 
     } 
    } 

    static void Main(string[] args) 
    { 
     var enumerator = new MyEnumerator(); 
     Scan(enumerator.Callback); 

     while (enumerator.MoveNext()) 
     { 
      Console.WriteLine(enumerator.Current); 
     } 
    } 

Bạn có thể bọc nó trong một đơn giản IEnumerable<Object>, nhưng tôi sẽ không khuyên bạn nên nó. IEnumerable danh sách ngụ ý rằng bạn có thể chạy nhiều điều tra viên trên cùng một danh sách, mà bạn không thể trong trường hợp này.

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