可以将回调调用转换为IEnumerable

时间:2011-02-10 10:01:24

标签: c# callback enumerable

我正在编写第三方库的包装器,它有一种方法来扫描它管理的数据。该方法采用一种回调方法,它调用它找到的数据中的每个项目。

e.g。该方法基本上是:void Scan(Action<object> callback);

我想将其换行并公开类似IEnumerable<object> Scan();

的方法

这是否可以不使用单独的线程来进行实际扫描和缓冲?

5 个答案:

答案 0 :(得分:4)

您应该调查Rx project - 这样可以将事件源用作IEnumerable

我不确定它是否允许香草回调如此呈现(它针对.NET事件)但值得一看,因为应该可以将常规回调呈现为IObservable

答案 1 :(得分:4)

您可以使用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);
        }
    }
}

请记住,这并不会像取消那样处理,因为回调方法并不真正允许它。一个适当的解决方案需要外部库的工作。

答案 2 :(得分: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列表意味着您可以在同一个列表中运行多个枚举器,在这种情况下您不能。

答案 3 :(得分:0)

这个怎么样:

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

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

    Scan(action);

    return objList;
}

答案 4 :(得分:-1)

查看yield关键字 - 这将允许您拥有一个看起来像IEnumerable但实际上为每个返回值进行处理的方法。