我正在编写一个围绕第三方库的包装器,它有一个扫描它管理的数据的方法。该方法接受一个回调方法,它为找到的数据中的每一项调用该回调方法。
例如,方法本质上是:void Scan(Action<object> callback);
我想包装它并公开一个像IEnumerable<object> Scan();这样的方法
如果没有单独的线程来执行实际的扫描和缓冲区,这是可能的吗?
发布于 2011-02-10 19:02:36
您可以非常简单地使用Reactive来完成此操作:
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);
}
}
}请记住,这不会处理取消之类的事情,因为回调方法实际上并不允许这样做。一个适当的解决方案需要在外部库部分进行工作。
发布于 2011-02-10 18:10:21
您应该调查Rx project -这允许将事件源作为IEnumerable使用。
我不确定它是否允许普通的回调以这种方式表示(它针对的是.NET事件),但它值得一看,因为它应该可以将常规的回调表示为IObservable。
发布于 2011-02-10 19:15:55
下面是一个阻塞枚举器( 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列表意味着您可以在同一列表上运行多个枚举器,这在本例中是不能的。
https://stackoverflow.com/questions/4955607
复制相似问题