Вот несколько решений в порядке убывания их полезности:
1. Использование default(CancellationToken)
по умолчанию:
Task DoAsync(CancellationToken ct = default(CancellationToken)) { … }
Семантически это CancellationToken.None
был бы идеальный кандидат на значение по умолчанию, но его нельзя использовать как таковой, потому что он не является константой времени компиляции. default(CancellationToken)
- следующая лучшая вещь, потому что это константа времени компиляции и официально задокументирована как эквивалентнаяCancellationToken.None
.
2. Предоставление перегрузки метода без CancellationToken
параметра:
Или, если вы предпочитаете перегрузку метода необязательным параметрам (см. Этот и этот вопрос по этой теме):
Task DoAsync(CancellationToken ct) { … }
Task DoAsync() => DoAsync(CancellationToken.None);
Для методов интерфейса то же самое можно сделать с помощью методов расширения:
interface IFoo
{
Task DoAsync(CancellationToken ct);
}
static class Foo
{
public static Task DoAsync(this IFoo foo) => foo.DoAsync(CancellationToken.None);
}
Это приводит к более тонкому интерфейсу и избавляет разработчиков от явного написания перегрузки метода пересылки.
3. Сделать параметр допускающим значение NULL и использовать в null
качестве значения по умолчанию:
Task DoAsync(…, CancellationToken? ct = null)
{
… ct ?? CancellationToken.None …
}
Мне нравится это решение хотя бы потому, что типы, допускающие значение NULL, связаны с небольшими издержками времени выполнения, а ссылки на токен отмены становятся более подробными из-за оператора объединения NULL ??
.
CancellationToken.None
станет чем-то большимdefault(CancellationToken)
.