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?

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.


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);
}

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 ??.


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;
    ...
}

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)

-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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.