Подтвердить что ты не робот

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

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

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

дает

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

и

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

Task<x> DoStuff(...., CancellationToken? ct = null) {
    var token = ct ?? CancellationToken.None;
    ...
}

Ответ 4

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

1. Использование default(CancellationToken) в качестве значения по умолчанию:

Task DoAsync(CancellationToken ct = default(CancellationToken)) { … }

Семантически, CancellationToken.None будет идеальным кандидатом для дефолта, но не может использоваться как таковой, потому что он не является константой времени компиляции. default(CancellationToken) является следующей лучшей вещью, потому что это константа времени компиляции и официально задокументирована как эквивалентная CancellationToken.None.

2. Предоставление метода перегрузки без параметра CancellationToken:

Или, если вы предпочитаете перегрузки метода по необязательным параметрам (см. this и этот вопрос по этой теме ):

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

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

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

Ответ 5

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

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

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

и перегрузить его как:

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

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