Параметр по умолчанию для CancellationToken

98

У меня есть асинхронный код, к которому я хотел бы добавить CancellationToken. Однако есть много реализаций, в которых это не нужно, поэтому я хотел бы иметь параметр по умолчанию - возможно CancellationToken.None. Однако,

Task<x> DoStuff(...., CancellationToken ct = null)

дает

Значение типа '' нельзя использовать в качестве параметра по умолчанию, поскольку нет стандартных преобразований в тип 'System.Threading.CancellationToken'

а также

Task<x> DoStuff(...., CancellationToken ct = CancellationToken.None)

Значение параметра по умолчанию для ct должно быть константой времени компиляции.

Есть ли способ установить значение по умолчанию для CancellationToken?

тофутим
источник

Ответы:

155

Оказывается, работает следующее:

Task<x> DoStuff(...., CancellationToken ct = default(CancellationToken))

...или:

Task<x> DoStuff(...., CancellationToken ct = default) // C# 7.1 and later

что, согласно документации , интерпретируется так же, как CancellationToken.None:

Вы также можете использовать default(CancellationToken)оператор C # для создания пустого токена отмены.

тофутим
источник
4
Именно этим фреймворк в настоящее время занимается внутри, но я бы не стал делать этого в своем коде. Подумайте, что произойдет с вашим кодом, если Microsoft изменит свою реализацию и CancellationToken.Noneстанет чем-то большим default(CancellationToken).
Носератио
10
@Noseratio Это сильно нарушит обратную совместимость, поэтому я бы не ожидал, что это произойдет. А что еще default(CancellationToken)делать?
svick
4
@Noseratio: Ты слишком жесткий. Скорее всего, CancellationToken.Noneони станут де-факто устаревшими. Даже Microsoft использует default(CancellationToken)вместо этого. Например, просмотрите эти результаты поиска в исходном коде Entity Framework.
drowa
21
Из свойства MSDN CancellationToken.None : «Вы также можете использовать инструкцию C # по умолчанию (CancellationToken) для создания пустого токена отмены» . Ни один из них не является ошибкой, пока будущая версия C # не примет его в качестве параметра по умолчанию.
MuiBienCarlota
5
То же самое здесь. Чтобы компенсировать две отсутствующие промежуточные комбинации, разработчики могут передать None или CancellationToken по умолчанию для параметра cancellationToken и null для параметра прогресса.
Арек Бал
25

Есть ли способ иметь значение по умолчанию для CancellationToken?

К сожалению, это невозможно, так как CancellationToken.Noneэто не константа времени компиляции, которая является требованием для значений по умолчанию в дополнительных аргументах.

Однако вы можете добиться того же эффекта, создав перегруженный метод вместо того, чтобы пытаться использовать параметры по умолчанию:

Task<x> DoStuff(...., CancellationToken ct)
{
    //...
}

Task<x> DoStuff(....)
{
    return DoStuff(...., CancellationToken.None);
}
Рид Копси
источник
6
Это рекомендуемый способ справиться с этим, как описано в документации по асинхронному шаблону на основе задач на MSDN (в частности, в разделе « Выбор перегрузок для предоставления» ).
Сэм Харвелл,
CancellationToken.None == по умолчанию true
eoleary
5
Что с тобой CancellationToken cancellationToken = default(CancellationToken)? Также здесь описано blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/…
Ray
20

Вот несколько решений в порядке убывания их полезности:

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 и использовать в nullкачестве значения по умолчанию:

Task DoAsync(…, CancellationToken? ct = null)
{
    … ct ?? CancellationToken.None …
}

Мне нравится это решение хотя бы потому, что типы, допускающие значение NULL, связаны с небольшими издержками времени выполнения, а ссылки на токен отмены становятся более подробными из-за оператора объединения NULL ??.

stakx - больше не участвует
источник
11

Другой вариант - использовать Nullable<CancellationToken>параметр по умолчанию nullи обрабатывать его внутри метода:

Task<x> DoStuff(...., CancellationToken? ct = null) {
    var token = ct ?? CancellationToken.None;
    ...
}
Тодд Менье
источник
молодец! безусловно, это лучший ответ
Джон Хенкель
6

Более новые версии C # допускают упрощенный синтаксис для версии по умолчанию (CancellationToken). Например:

Task<x> DoStuff(...., CancellationToken ct = default)
Алексей
источник
-1

Ответ Тофутима односторонний, но из комментариев я вижу, что у людей есть проблемы с этим.

В этом случае я предположил, что можно использовать следующий метод:

Task<x> DoStuff(...., CancellationToken ct)
{
} 

и перегружаем его как:

Task<x> DoStuff(....)
{
    return DoStuff(...., CancellationToken.None);
}

Это компилируется, потому что значение CancellationToken.Noneне требуется во время компиляции.

Озаир Кафрай
источник