2017-07-24 28 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'返回方法和委託中全面異常處理的重要性。 –