Вот несколько решений в порядке убывания их полезности:
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).