Ось кілька рішень у порядку зменшення загальної доброти:
1. Використання default(CancellationToken)
як значення за замовчуванням:
Task DoAsync(CancellationToken ct = default(CancellationToken)) { … }
Семантично, CancellationToken.None
це буде ідеальним кандидатом за замовчуванням, але не може бути використано як таке, оскільки це не константа часу компіляції. default(CancellationToken)
є наступною найкращою річчю, оскільки це константа часу компіляції і офіційно задокументована як еквівалентCancellationToken.None
.
2. Забезпечення перевантаження методу без CancellationToken
параметра:
Або, якщо ви віддаєте перевагу перевантаженню методів над необов’язковими параметрами (див. Це та це питання на цю тему):
Task DoAsync(CancellationToken ct) { … } // actual method always requires a token
Task DoAsync() => DoAsync(CancellationToken.None); // overload producing a default token
Щодо методів інтерфейсу, того ж можна досягти за допомогою методів розширення:
interface IFoo
{
Task DoAsync(CancellationToken ct);
}
static class Foo
{
public static Task DoAsync(this IFoo foo) => foo.DoAsync(CancellationToken.None);
}
Це призводить до тоншого інтерфейсу і позбавляє розробників від явного написання перевантаження методу пересилання.
3. Зробити параметр нульовим і використовувати null
як значення за замовчуванням:
Task DoAsync(…, CancellationToken? ct = null)
{
… ct ?? CancellationToken.None …
}
Мені це рішення подобається найменше тому, що типи, що допускають обнулення, мають невеликі накладні витрати на виконання, а посилання на маркер скасування стають більш багатослівними через нульовий оператор злиття ??
.
CancellationToken.None
стане чимось більшеdefault(CancellationToken)
.