I have a legacy class which I would like to consume in a more convenient way.
class Camera
{
void RequestCapture();
public delegate void ReadResultEventHandler(ControllerProxy sender, ReadResult readResult);
public event ReadResultEventHandler OnReadResult;
}
The class manages a camera that takes pictures. It works by:
The operation is asynchronous. The RequestCapture is a fire-and-forget operation (fast.) operation, After some seconds. 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.
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
}
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. 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. 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
You could convert the Camera.OnReadResult
event to an observable sequence using the FromEvent
operator:
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. 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);
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.