简体   繁体   English

从旧版 class(启动方法 + 事件)创建任务(或 Observable)

[英]Create a Task (or Observable) from legacy class (start method + events)

I have a legacy class which I would like to consume in a more convenient way.我有一个旧版 class,我想以更方便的方式使用它。

class Camera 
{
     void RequestCapture();
     public delegate void ReadResultEventHandler(ControllerProxy sender, ReadResult readResult);
     public event ReadResultEventHandler OnReadResult;
}

The class manages a camera that takes pictures. class 管理一个拍照的相机。 It works by:它的工作原理是:

  1. Invoking the RequestCapture method调用 RequestCapture 方法
  2. Awaiting for the OnReadResult to be raised (obviously, you need to subscribe to this event in order to get the captured data)等待引发 OnReadResult(显然,您需要订阅此事件才能获取捕获的数据)

The operation is asynchronous.该操作是异步的。 The RequestCapture is a fire-and-forget operation (fast.) operation, After some seconds. RequestCapture 是一种即发即弃操作(快速)操作,几秒钟后。 the event will raise.该事件将引发。

I would like to consume it either like as regular Task or as an IObservable<ReadResult> but I'm quite lost, since it doesn't adapt to the Async Pattern, and I have never used a class like this.我想像常规TaskIObservable<ReadResult>一样使用它,但我很迷茫,因为它不适应异步模式,而且我从未使用过这样的 class。

Following the example in this article my rudimentary approach (untested:!) would be:按照本文中的示例,我的基本方法(未经测试:!)将是:

class AsyncCamera
{
    private readonly Camera camera;
    
    public AsyncCamera(Camera camera)
    {
        this.camera = camera ?? throw new ArgumentNullException(nameof(camera));
    } 

    public Task<ReadResult> CaptureAsync()
    {
         TaskCompletionSource<ReadResult> tcs = new TaskCompletionSource<ReadResult>();
         camera.OnReadResult += (sender, result) => {
             tcs.TrySetResult(result);
         };
         camera.RequestCapture();
         return tcs.Task;
    }
}

Possible usage:可能的用法:

public async Task DoSomethingAsync()
{
    var asyncCam = new AsyncCamera(new Camera());
    var readResult = await asyncCam.CaptureAsync();
    // use readResult
}

EDIT taking Stephen's comment into account (THANK you!)编辑考虑斯蒂芬的评论(谢谢!)

So, this is my naive take on unsubscribing the event handler.所以,这是我对取消订阅事件处理程序的天真看法。 I didn't have the time to make any tests, so suggestions for improvement are welcome.我没有时间进行任何测试,因此欢迎提出改进建议。

class AsyncCamera
{
    private readonly Camera camera;
    
    public AsyncCamera(Camera camera)
    {
        this.camera = camera ?? throw new ArgumentNullException(nameof(camera));
    } 

    public Task<ReadResult> CaptureAsync()
    {
         ReadResultEventHandler handler = null;
         TaskCompletionSource<ReadResult> tcs = new TaskCompletionSource<ReadResult>();

         handler = (sender, result) => {
             camera.OnReadResult -= handler;
             tcs.TrySetResult(result);
         };

         camera.OnReadResult += handler;
         camera.RequestCapture();
         return tcs.Task;
    }
}

In case comments get "cleaned up": Stephen said以防评论被“清理”:斯蒂芬说

Unsubscription isn't handled in the MS examples, but it really should be. MS 示例中未处理取消订阅,但确实应该处理。 You'd have to declare a variable of type ReadResultDelegate (or whatever it's called), set it to null, and then set it to the lambda expression which can then unsubscribe that variable from the event.您必须声明一个 ReadResultDelegate 类型的变量(或其他任何名称),将其设置为 null,然后将其设置为 lambda 表达式,然后该表达式可以从事件中取消订阅该变量。 I don't have an example of this on my blog, but there's a general-purpose one here .我的博客上没有这方面的例子,但这里有一个通用的例子。 Which, now that I look at it, does not seem to handle cancellation appropriately.哪个,现在我看着它,似乎没有适当地处理取消。 Stephen Cleary ——斯蒂芬·克利里

Emphasis by me.由我强调。

Seems to work: https://dotnetfiddle.net/9XsaUB似乎工作: https://dotnetfiddle.net/9XsaUB

You could convert the Camera.OnReadResult event to an observable sequence using the FromEvent operator:您可以使用FromEvent运算符将Camera.OnReadResult事件转换为可观察序列:

var camera = new Camera();

IObservable<ReadResult> observable = Observable
    .FromEvent<Camera.ReadResultEventHandler, ReadResult>(
        h => (sender, readResult) => h(readResult),
        h => camera.OnReadResult += h,
        h => camera.OnReadResult -= h);

Alternatively you could use the Create operator.或者,您可以使用Create运算符。 It's more verbose, but also probably easier to comprehend.它更冗长,但也可能更容易理解。

IObservable<ReadResult> observable = Observable.Create<ReadResult>(o =>
{
    camera.OnReadResult += Camera_OnReadResult;
    void Camera_OnReadResult(ControllerProxy sender, ReadResult readResult)
    {
        o.OnNext(readResult);
    }
    return Disposable.Create(() => camera.OnReadResult -= Camera_OnReadResult);
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM