Насколько я понимаю, ваше желание состоит в том, чтобы иметь возможность игнорировать более 1 типа исключений в ожидании вашего Task
. Ваше собственное решение кажется вашим лучшим вариантом для меня. Вы всегда можете просто «связать» вызовы, используя предложенное решение:
await myTask.Ignore<OperationCanceledException>().Ignore<IOException>().Ignore<TimeoutException>();
Это должно вернуть задачу, которая, по сути, представляет собой три вложенных блока try-catch. Если вы активно не хотите более элегантного определения , вы всегда можете уйти с более элегантным использованием ;)
Единственное, что не такэлегантная проблема заключается в том, что в случае TResult
-возврата Task
s вам придется «распространять» значение по умолчанию несколько раз. Если это не очень большая проблема, то вы можете получить то же самое:
await myTask.Ignore<int, OperationCanceledException>(0).Ignore<int, TimeoutException>(0);
В качестве "неясного" бонуса, обратите внимание, что таким образом вы можете очень легко предоставить различные возвращаемые значения по умолчанию для разные исключения . Так что необходимость повторения значения по умолчанию может в конечном итоге привести к вашей выгоде! Например, у вас может быть причина для возврата 0 в TimeOutException, но -1 в OperationCanceledException и т. Д. Если в итоге это становится вашей целью , помните, что использование is
может оказаться не тем, что вам действительно нужно, скореечем точное Type
равенство, потому что вы можете также хотеть возвратить разные значения по умолчанию для разных исключений, которые происходят из одного и того же Type
(этот анализ начинает становиться довольно сложным, но вы, конечно, понимаете, в чем дело).
Обновление
Максимальный уровень элегантности цепочки вызовов для TResult
версии, похоже, достигается за счет проверки типов во время компиляции:
public static async Task<TResult> Ignore<TResult, TException>(
this Task<TResult> task, TResult defaultValue)
where TException : Exception
{
try
{
return await task;
}
catch (Exception ex)
{
if (ex is TException) return defaultValue;
throw;
}
}
public static async Task<TResult> Ignore<TResult, TException>(
this Task task, TResult defaultValue)
where TException : Exception
{
try
{
return await (Task<TResult>)task;
}
catch (Exception ex)
{
if (ex is TException) return defaultValue;
throw;
}
}
public static Task Ignore<TException>(this Task task)
where TException : Exception
{
try
{
//await seems to create a new Task that is NOT the original task variable.
//Therefore, trying to cast it later will fail because this is not a Task<TResult>
//anymore (the Task<TResult> has been "swallowed").
//For that reason, await the task in an independent function.
Func<Task> awaitableCallback = async () => await task;
awaitableCallback();
//And return the original Task, so that it can be cast back correctly if necessary.
return task;
}
catch (Exception ex)
{
//Same upon failure, return the original task.
if (ex is TException) return task;
throw;
}
}
public static async Task<int> TestUse()
{
Task<int> t = Task<int>.Run(() => 111);
int result = await t.Ignore<TaskCanceledException>()
.Ignore<InvalidOperationException>()
.Ignore<int, TimeoutException>(0);
return result;
}
Если вы готовы пожертвовать безопасностью во время компиляции, вы можете облегчить боль повторения, только указав исключения, которые вы хотите игнорировать, и добавив вызов «casting» в конце. Конечно, у этого есть свои проблемы, но вам нужно делать это только тогда, когда вам нужно игнорировать множественные исключения. В противном случае вам подходит один тип и соответствующий одиночный вызов Ignore<TResult, TException>()
.
Редактировать
На основе соответствующего комментария , поскольку шаблон асинхронного ожидания / ожиданиякажется, порождает новую задачу, которая оборачивает ожидаемый параметр задачи, переданный в методах Ignore выше, вызов примера действительно завершается с InvalidCastException
, так как промежуточные вызовы игнорирования фактически изменили , задача и исходная задача получаетпотерял где-то в цепочке звонков. Поэтому метод «casting» Ignore
был немного изменен, чтобы позволить возвращать исходное задание в конце, чтобы его можно было успешно откатить после всех вызовов Ignore
по последним TResult
на основе Ignore
звонок. Код выше был изменен, чтобы исправить этот сценарий. Это не делает весь шаблон особенно элегантным, но, по крайней мере, сейчас он работает правильно.