2017-07-24 31 views
4

Disposable.Create需要Action作为参数。 Action在处理Rx订阅时运行。Async Disposable.Create

当处理Rx订阅时,我想运行一些异步清理代码,但使用async() =>Actionasync void相同,我想避免这种情况。有关我为什么要避免这种情况的更多详细信息,请参阅here

是否可以创建类似Disposable.AsyncCreate的东西,它接受Func<Task>而不是Action。如果是这样,我应该如何使用它作为CompositeDisposable的一部分?

或者还有其他模式用于处理异步处置吗?

+2

IDisposable接口没有任何方法返回Task,因此应如何调用Dispose等待任务? –

+0

这个问题与以前的问题有何不同:https://stackoverflow.com/questions/45205132/alternative-to-using-async-in-rx-finally? – VMAtm

+0

@Peter也是我的理解。不知道是否有某种方法可以完成这个任务。 –

回答

3

你可以做这样的事情。我还不能肯定知道这是多么好:

public class DisposableAsync 
{ 
    private readonly IDisposable _disposable; 
    private readonly Func<Task> _asyncDisposalAction; 
    public DisposableAsync(IDisposable disposable, Func<Task> asyncDisposalAction) 
    { 
     _disposable = disposable; 
     _asyncDisposalAction = asyncDisposalAction; 
    } 

    public Task DisposeAsync() 
    { 
     _disposable.Dispose(); 
     return _asyncDisposalAction(); 
    } 
} 

public static class DisposableAsyncExtensions 
{ 
    public static DisposableAsync ToAsync(this IDisposable disposable, Func<Task> asyncDisposalAction) 
    { 
     return new DisposableAsync(disposable, asyncDisposalAction); 
    } 
} 

然后,您可以使用这样的:

async Task Go() 
{ 

    var o = Observable.Interval(TimeSpan.FromMilliseconds(100)); 
    var d = o 
     .Subscribe(i => Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: {i}")) 
     .ToAsync(async() => 
     { 
      Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Dispose Beginning"); 
      await Task.Delay(1000); 
      Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Dispose Complete"); 
     }); 
    Console.Read(); 
    var t = d.DisposeAsync(); 
    Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Outside task, waiting for dispose to complete"); 
    await t; 
    Console.WriteLine($"{DateTime.Now.ToLongTimeString()}: Task Complete"); 

} 

该解决方案不会与using()语句的工作,和类DisposableAsync应有些鲁莽。除此之外,我无法想到它有什么问题,但我却对此置之不理。只是觉得有点哈克。

+0

既然你没有使用''using',你应该仍然使用'try/finally'(作为'using')来确保处置发生,即使有异常。 – Servy

+0

这不完全是我想象的解决方案,但它最接近我也来的答案。 –

1

我不认为async() =>有你认为它存在的问题。

试试这个:

Action x = async() => 
{ 
    try 
    { 
     Console.WriteLine("Hello"); 
     await Task.Delay(TimeSpan.FromSeconds(2.0)); 
     throw new Exception("Wait"); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 
    } 
    Console.WriteLine("Goodbye"); 
}; 

var d = Disposable.Create(x); 

d.Dispose(); 

它产生:

 
Hello 
Wait 
Goodbye 
+0

我想这是因为'Action'里面的所有东西都包含在try/catch中,因此没有任何异常风险被抛出'Action'之外,因此不存在这样的异常崩溃的风险。我想这可能会奏效,但在我看来,这不是最直观的美观解决方案。 –

+0

如果您将代理外部的try catch移至dispose周围,则会在该进程上收到未处理的异常。 – Shlomo

+0

为了培养良好的编码习惯,你会考虑将'await'移动到'try {}'体内吗?很难夸大在非'Task'返回方法和委托中全面异常处理的重要性。 –