В целом необходимо одобрить интерфейсы по абстрактным классам. Одна причина использовать абстрактный класс состоит в том, если у Вас есть общая реализация среди реальных классов. Конечно, необходимо все еще объявить интерфейс (IPet) и иметь абстрактный класс (PetBase) реализация тот интерфейс. Используя маленькие, отличные интерфейсы, можно использовать кратные числа для дальнейшего улучшения гибкости. Интерфейсы позволяют максимальное количество гибкости и мобильность типов через границы. Когда передающие ссылки через границы, всегда передавайте интерфейс а не конкретный тип. Это позволяет принимающему концу определять конкретную реализацию и обеспечивает максимальную гибкость. Это абсолютно верно при программировании способом TDD/BDD.
Банда Четырех указанных в их книге, "Поскольку наследование представляет подкласс деталям реализации его родителя, часто говорится, что 'наследование повреждает инкапсуляцию". Я полагаю, что это верно.
Это - немного расширенная версия предыдущих ответов.
CancellationToken
для исходной задачи, и когда тайм-аут происходит, Вы добираетесь TimeoutException
вместо OperationCanceledException
. async Task<TResult> CancelAfterAsync<TResult>(Func<CancellationToken, Task<TResult>> startTask, TimeSpan timeout, CancellationToken cancellationToken)
{
using (var timeoutCancellation = new CancellationTokenSource())
using (var combinedCancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCancellation.Token))
{
var originalTask = startTask(combinedCancellation.Token);
var delayTask = Task.Delay(timeout, timeoutCancellation.Token);
var completedTask = await Task.WhenAny(originalTask, delayTask);
// Cancel timeout to stop either task:
// - Either the original task completed, so we need to cancel the delay task.
// - Or the timeout expired, so we need to cancel the original task.
// Canceling will not affect a task, that is already completed.
timeoutCancellation.Cancel();
if (completedTask == originalTask)
{
// original task completed
return await originalTask;
}
else
{
// timeout
throw new TimeoutException();
}
}
}
Я - recombinging идеи некоторых других ответов здесь и этот ответ на другом потоке в метод расширения стиля Попытки. Это обладает преимуществом, если Вы хотите дополнительный метод, уже избегая исключения на тайм-аут.
public static async Task<bool> TryWithTimeoutAfter<TResult>(this Task<TResult> task,
TimeSpan timeout, Action<TResult> successor)
{
using var timeoutCancellationTokenSource = new CancellationTokenSource();
var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token))
.ConfigureAwait(continueOnCapturedContext: false);
if (completedTask == task)
{
timeoutCancellationTokenSource.Cancel();
// propagate exception rather than AggregateException, if calling task.Result.
var result = await task.ConfigureAwait(continueOnCapturedContext: false);
successor(result);
return true;
}
else return false;
}
async Task Example(Task<string> task)
{
string result = null;
if (await task.TryWithTimeoutAfter(TimeSpan.FromSeconds(1), r => result = r))
{
Console.WriteLine(result);
}
}