可以将回调调用转换为 IEnumerable

发布于 2024-10-16 18:35:25 字数 265 浏览 8 评论 0原文

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

例如,该方法本质上是: void Scan(Actioncallback);

我想包装它并公开类似 IEnumerable; 的方法。 Scan();

这是否可以在不诉诸单独的线程来执行实际扫描和缓冲区的情况下实现?

I'm writing a wrapper around a 3rd party library, and it has a method to scan the data it manages. The method takes a callback method that it calls for each item in the data that it finds.

e.g. The method is essentially: void Scan(Action<object> callback);

I want to wrap it and expose a method like IEnumerable<object> Scan();

Is this possible without resorting to a separate thread to do the actual scan and a buffer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

勿忘初心 2024-10-23 18:35:25

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

请记住,这不会处理诸如取消之类的事情,因为回调方法实际上不允许这样做。正确的解决方案需要外部库的工作。

You can do this quite simply with 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);
        }
    }
}

Remember that this doesn't take care of things like cancellation, since the callback method doesn't really allow it. A proper solution would require work on the part of the external library.

铁憨憨 2024-10-23 18:35:25

您应该调查 Rx 项目 — 这允许将事件源用作IEnumerable

我不确定它是否允许像这样呈现普通回调(它针对 .NET 事件),但值得一看,因为应该可以将常规回调呈现为 IObservable 。

You should investigate the Rx project — this allows an event source to be consumed as an IEnumerable.

I'm not sure if it allows vanilla callbacks to be presented as such (it's aimed at .NET events) but it would be worth a look as it should be possible to present a regular callback as an IObservable.

残疾 2024-10-23 18:35:25

这是一个阻塞枚举器(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中,但我不推荐它。 IEnumerable 列表意味着您可以在同一个列表上运行多个枚举器,但在本例中则不能。

Here is a blocking enumerator (the Scan method needs to run in a separate thread)

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

You could wrap it in a simple IEnumerable<Object>, but I would not recommend it. IEnumerable lists implies that you can run multiple enumerators on the same list, which you can't in this case.

羁客 2024-10-23 18:35:25

这个怎么样:

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

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

    Scan(action);

    return objList;
}

How about this one:

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

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

    Scan(action);

    return objList;
}
小兔几 2024-10-23 18:35:25

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

Take a look at the yield keyword -- which will allow you to have a method that looks like an IEnumerable but which actually does processing for each return value.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文