Domyślny parametr dla CancellationToken

96

Mam kod asynchroniczny, do którego chciałbym dodać CancellationToken. Jest jednak wiele implementacji, w których nie jest to potrzebne, więc chciałbym mieć parametr domyślny - być może CancellationToken.None. Jednak,

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

plony

Wartość typu „” nie może być używana jako parametr domyślny, ponieważ nie ma standardowych konwersji na typ „System.Threading.CancellationToken”

i

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

Domyślna wartość parametru dla „ct” musi być stałą czasu kompilacji

Czy istnieje sposób na uzyskanie wartości domyślnej CancellationToken?

tofutim
źródło

Odpowiedzi:

154

Okazuje się, że działa:

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

...lub:

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

który zgodnie z dokumentacją jest interpretowany tak samo, jak CancellationToken.None:

Możesz również użyć instrukcji C # default(CancellationToken), aby utworzyć pusty token anulowania.

tofutim
źródło
4
To jest dokładnie to, co obecnie robi framework wewnętrznie, ale nie robiłbym tego w swoim kodzie. Pomyśl, co stanie się z Twoim kodem, jeśli Microsoft zmieni ich implementację i stanie CancellationToken.Nonesię czymś więcej niż default(CancellationToken).
noseratio
10
@Noseratio To w dużym stopniu złamałoby wsteczną kompatybilność, więc nie spodziewałbym się, że tak się stanie. A co innego by default(CancellationToken)zrobił?
Svick
4
@Noseratio: Jesteś zbyt sztywny. Prawdopodobnie CancellationToken.Nonestaną się de facto przestarzałe. Nawet Microsoft używa default(CancellationToken)zamiast tego. Na przykład zobacz te wyniki wyszukiwania z kodu źródłowego Entity Framework.
drowa
21
Z właściwości CancellationToken.None MSDN : „Można również użyć instrukcji C # default (CancellationToken), aby utworzyć pusty token anulowania” . Żaden nie jest błędem, dopóki futurystyczna wersja C # nie zaakceptuje go jako parametru domyślnego.
MuiBienCarlota
5
To samo tutaj Aby zrekompensować dwie brakujące kombinacje pośrednie, programiści mogą przekazać None lub domyślny CancellationToken dla parametru cancellationToken i null dla parametru progress.
Arek Bal
25

Czy istnieje sposób, aby mieć wartość domyślną CancellationToken?

Niestety nie jest to możliwe, ponieważ CancellationToken.Nonenie jest to stała czasowa kompilacji, która jest wymagana dla wartości domyślnych w opcjonalnych argumentach.

Możesz jednak zapewnić ten sam efekt, wykonując przeciążoną metodę zamiast próbować używać parametrów domyślnych:

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

Task<x> DoStuff(....)
{
    return DoStuff(...., CancellationToken.None);
}
Reed Copsey
źródło
6
Jest to zalecany sposób obsługi tego problemu, jak wyjaśniono w dokumentacji wzorca asynchronicznego opartego na zadaniach w witrynie MSDN (w szczególności w sekcji Wybieranie przeciążeń do dostarczenia ).
Sam Harwell
CancellationToken.None == default true
eoleary
5
O co chodzi CancellationToken cancellationToken = default(CancellationToken)? Opisano również tutaj blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/…
Ray
20

Oto kilka rozwiązań, w porządku malejącym według ogólnej dobroci:

1. Używanie default(CancellationToken)jako wartości domyślnej:

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

Semantycznie CancellationToken.Nonebyłby idealnym kandydatem na wartość domyślną, ale nie można go używać jako takiego, ponieważ nie jest to stała czasu kompilacji. default(CancellationToken)jest następną najlepszą rzeczą, ponieważ jest stałą czasu kompilacji i oficjalnie udokumentowano jej odpowiednikCancellationToken.None .

2. Zapewnienie przeciążenia metody bez CancellationTokenparametru:

Lub, jeśli wolisz przeciążenie metod zamiast parametrów opcjonalnych (zobacz to i to pytanie na ten temat):

Task DoAsync(CancellationToken ct) { … } // actual method always requires a token
Task DoAsync() => DoAsync(CancellationToken.None); // overload producing a default token

W przypadku metod interfejsu to samo można osiągnąć za pomocą metod rozszerzających:

interface IFoo
{
    Task DoAsync(CancellationToken ct);
}

static class Foo
{
    public static Task DoAsync(this IFoo foo) => foo.DoAsync(CancellationToken.None);
}

Powoduje to szczuplejszy interfejs i oszczędza implementatorom jawnego pisania przeciążenia metody przekazywania.

3. Wprowadzanie wartości null dla parametru i używanie nulljako wartości domyślnej:

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

To rozwiązanie podoba mi się przynajmniej dlatego, że typy dopuszczające wartość null mają niewielki narzut w czasie wykonywania, a odwołania do tokenu anulowania stają się bardziej szczegółowe z powodu operatora łączenia wartości null ??.

stakx - już nie wnosi wkładu
źródło
11

Inną opcją jest użycie Nullable<CancellationToken>parametru, ustawienie domyślne nulli zajęcie się nim wewnątrz metody:

Task<x> DoStuff(...., CancellationToken? ct = null) {
    var token = ct ?? CancellationToken.None;
    ...
}
Todd Menier
źródło
znakomity! jak dotąd jest to najlepsza odpowiedź
John Henckel
6

Nowsze wersje języka C # pozwalają na uproszczoną składnię dla wersji domyślnej (CancellationToken). Na przykład:

Task<x> DoStuff(...., CancellationToken ct = default)
Alexei
źródło
-1

Odpowiedź Tofutima jest jedna, ale z komentarzy widzę, że ludzie mają z nią problemy.

W takim przypadku zasugerowałem, że można mieć następującą metodę:

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

i przeciąż go jako:

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

To kompiluje się, ponieważ wartość CancellationToken.Nonenie jest wymagana w czasie kompilacji.

Ozair Kafray
źródło