2011-02-10 13 views
8

私はサードパーティ製のライブラリの周りにラッパーを作成しており、管理しているデータをスキャンする方法があります。このメソッドは、見つかったデータの各項目を呼び出すコールバックメソッドを呼び出します。コールバックコールをIEnumerableに変換する可能性があります

この方法は、本質的である:void Scan(Action<object> callback);

私はそれをラップし、IEnumerable<object> Scan();

のような方法を公開する実際のスキャンおよびバッファを行うために別のスレッドに頼ることなく、可能なこのですか?

+0

サードパーティライブラリの正確なフローを説明できますか?既に複数のスレッドに生成されているようです。また、マルチキャストの代理人がいるかもしれないことに注意してください。また、スキャンが終了したことをどのように判断していますか? – weismat

+0

正直言って、私はこれを行う際に値を見ることができません。もしあなたが 'foreach(scannable.Scan())のオブジェクトobj {/ * obj * /}で何かする' 'を実行したいのであれば、' scannable.Scan(obj => {/ * obj * /}); '? – Flynn1179

+0

@ weismat:いいえ、スレッドを生成しません。基本的に内部的にデータストアに対して反復処理を行い、各レコードのコールバックを呼び出します。 – Gareth

答えて

4

あなたは反応性に非常に単純にこれを行うことができます。

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); 
     } 
    } 
} 

は、コールバックメソッドは、実際にそれを許可していないので、これは、キャンセルのようなものの世話をしないことに注意してください。適切な解決方法では、外部ライブラリの一部での作業が必要になります。ここで

+0

うーん、それはうまく動作します。私は、別のスレッドを使わなければならないことを避けたいと考えましたが、1つのスレッドだけを使用することはできません。これは最も簡単な方法です。ありがとう。 – Gareth

+0

ええ、実際には1つのスレッドだけを使用してEnumerableに取り込むことは実際には不可能だと思います。私が得ることができる最も近いObservableでしたが、その場合は別の名前でコールバックです:) – porges

-1

キーワードyieldを参照してください。IEnumerableのように見えるメソッドを使用できますが、各戻り値に対して実際に処理します。 1これについてどのように

+1

はい、私はOPがそのようなフック方法を知りたいと思います'Action 'コールバックまでのメソッドです。 – LukeH

+0

ああ、私はあなたが意味するものを見る - はるかに困難な質問! –

+0

ええ、最初の考えは 'Scan(x => {yield return x;});'という1行の方法でしたが、残念ながらyieldはそれほどうまくいきません。 – Flynn1179

0

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

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

    Scan(action); 

    return objList; 
} 
+1

コールバックが非同期を実行するので、 'Scan(action)'はアイテムを追加し、返された 'IEnumerable'は反復され、例外が発生する可能性があります。 – Cornelius

+0

それはうまくいきますが、コールバックの潜在的な数は何百万ものものになる可能性がありますので、繰り返し処理する前に全体をバッファリングしないでください。 – Gareth

4

あなたが調査する必要がRx project - これはイベント・ソースがIEnumerableとして消費することができます。

バニラコールバックが(.NETイベントを対象にして)表示されるかどうかはわかりませんが、通常のコールバックをIObservableとして提示する必要があります。

2

はブロッキング列挙子である(Scanメソッドは別のスレッドで実行する必要がある)

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); 
     } 
    } 

あなたは、単純なIEnumerable<Object>でそれをラップすることができますが、私はそれをお勧めしません。 IEnumerableリストは、同じリストで複数の列挙子を実行できることを意味します。