我正在尝试做相当于下降的C#5伪代码: -
async Task<int> CallAndTranslate()
{
try
{
return await client.CallAsync();
} catch(FaultException ex) {
if (ex.FaultCode ...)
throw new Exception("translated");
}
}
Given an arbitrary Task
which does not return a result, translating exceptions从C#5向后移植很容易using the technique supplied by @Drew Marsh
这项技术不会琐碎地概括为Task<T>
,因为我Task.ContinueWith
的任何重载都会返回一个光头Task
,而不是Task<T>
。
有没有办法使用TPL API实现这一点,而不必诉诸:
Task<T>
这是我天真的占位符实现:
public class TranslatingExceptions
{
Task<int> ApiAsync()
{
return Task<int>.Factory.StartNew( () => {
throw new Exception( "Argument Null" ); } );
}
public Task<int> WrapsApiAsync()
{
return ApiAsync().TranslateExceptions(x=>{
if (x.Message == "Argument Null" )
throw new ArgumentNullException();
});
}
[Fact]
public void Works()
{
var exception = Record.Exception( () =>
WrapsApiAsync().Wait() );
Assert.IsType<ArgumentNullException>( exception.InnerException );
}
}
以下Task<T>
扩展实现了我的占位符实现:
static class TaskExtensions
{
public static Task<T> TranslateExceptions<T>( this Task<T> task, Action<Exception> translator )
{
// TODO REPLACE NAIVE IMPLEMENTATION HERE
return Task<T>.Factory.StartNew( () =>
{
try
{
return task.Result;
}
catch ( AggregateException exception )
{
translator( exception.InnerException );
throw;
}
} );
}
}
答案 0 :(得分:2)
您可以使用迭代器(await
)在.NET 4.0中模仿yield
,但它并不漂亮。
如果没有状态机,你就会错过await
的全部内容,即在工作完成之前将控制权返回给调用者,然后才继续执行。
ContinueWith<T>
,只需稍微调整Drew Marsh的代码即可:
public Task<int> ApiAsync() // The inner layer exposes it exactly this way
{
return Task<int>.Factory.StartNew( () =>
{ throw new Exception( "Argument Null" ); } );
}
// this layer needs to expose it exactly this way
public Task<int> WrapsApiAsync()
{
// Grab the task that performs the "original" work
Task<int> apiAsyncTask = ApiAsync();
// Hook a continuation to that task that will do the exception "translation"
Task<int> result = apiAsyncTask.ContinueWith( antecedent =>
{
// Check if the antecedent faulted
// If so check what the exception's message was
if ( antecedent.IsFaulted )
{
if ( antecedent.Exception.InnerException.Message == "Argument Null" )
{
throw new ArgumentNullException();
}
throw antecedent.Exception.InnerException;
}
return antecedent.Result;
},
TaskContinuationOptions.ExecuteSynchronously );
// Now we return the continuation Task from the wrapper method
// so that the caller of the wrapper method waits on that
return result;
}
更新:使用TaskCompletionSource
的示例public static Task<int> WrapsApiAsync()
{
var tcs = new TaskCompletionSource<int>();
Task<int> apiAsyncTask = ApiAsync();
apiAsyncTask.ContinueWith( t =>
{
switch ( t.Status )
{
case TaskStatus.RanToCompletion:
tcs.SetResult( task.Result );
break;
case TaskStatus.Canceled:
tcs.SetCanceled();
break;
case TaskStatus.Faulted:
if ( t.Exception.InnerException.Message == "Argument Null" )
{
try
{
throw new ArgumentNullException();
}
catch ( ArgumentNullException x )
{
tcs.SetException( x );
}
}
else
{
tcs.SetException( t.Exception.InnerException );
}
break;
}
}
);
return tcs.Task;
}