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
?
CancellationToken.None
się czymś więcej niżdefault(CancellationToken)
.default(CancellationToken)
zrobił?CancellationToken.None
staną się de facto przestarzałe. Nawet Microsoft używadefault(CancellationToken)
zamiast tego. Na przykład zobacz te wyniki wyszukiwania z kodu źródłowego Entity Framework.Niestety nie jest to możliwe, ponieważ
CancellationToken.None
nie 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); }
źródło
CancellationToken cancellationToken = default(CancellationToken)
? Opisano również tutaj blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/…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.None
był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
CancellationToken
parametru: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
null
jako 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
??
.źródło
Inną opcją jest użycie
Nullable<CancellationToken>
parametru, ustawienie domyślnenull
i zajęcie się nim wewnątrz metody:Task<x> DoStuff(...., CancellationToken? ct = null) { var token = ct ?? CancellationToken.None; ... }
źródło
Nowsze wersje języka C # pozwalają na uproszczoną składnię dla wersji domyślnej (CancellationToken). Na przykład:
Task<x> DoStuff(...., CancellationToken ct = default)
źródło
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.None
nie jest wymagana w czasie kompilacji.źródło