Parameter default untuk CancellationToken

98

Saya memiliki beberapa kode asinkron yang ingin saya tambahkan CancellationToken. Namun, ada banyak implementasi yang tidak memerlukannya, jadi saya ingin memiliki parameter default - mungkin CancellationToken.None. Namun,

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

hasil

Nilai bertipe '' tidak dapat digunakan sebagai parameter default karena tidak ada konversi standar untuk mengetik 'System.Threading.CancellationToken'

dan

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

Nilai parameter default untuk 'ct' harus berupa konstanta waktu kompilasi

Apakah ada cara untuk memiliki nilai default CancellationToken?

tofutim
sumber

Jawaban:

155

Ternyata yang berikut berfungsi:

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

...atau:

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

yang menurut dokumentasinya diartikan sama dengan CancellationToken.None:

Anda juga dapat menggunakan pernyataan C # default(CancellationToken)untuk membuat token pembatalan kosong.

tofutim
sumber
4
Ini persis seperti yang dilakukan framework saat ini secara internal, tetapi saya tidak akan melakukannya dalam kode saya. Pikirkan apa yang terjadi dengan kode Anda jika Microsoft mengubah implementasinya, dan CancellationToken.Nonemenjadi sesuatu yang lebih dari itu default(CancellationToken).
noseratio
11
@Noseratio Itu akan merusak kompatibilitas ke belakang secara besar-besaran, jadi saya tidak berharap itu terjadi. Dan apa lagi yang akan default(CancellationToken)dilakukan?
svick
4
@Noseratio: Anda terlalu kaku. Kemungkinan CancellationToken.Nonede facto tidak akan digunakan lagi. Bahkan Microsoft menggunakan default(CancellationToken)sebagai gantinya. Misalnya, lihat hasil pencarian ini dari kode sumber Kerangka Entitas.
drowa
21
Dari Properti MSDN CancellationToken.None : "Anda juga dapat menggunakan pernyataan C # default (CancellationToken) untuk membuat token pembatalan kosong" . Tidak ada yang salah sampai versi C # berikutnya menerimanya sebagai parameter default.
MuiBienCarlota
5
Sama di sini Untuk mengkompensasi dua kombinasi perantara yang hilang, pengembang dapat meneruskan None atau CancellationToken default untuk parameter cancellationToken dan null untuk parameter kemajuan.
Arek Bal
25

Apakah ada cara untuk memiliki nilai default untuk CancellationToken?

Sayangnya, ini tidak mungkin, karena CancellationToken.Nonebukan konstanta waktu kompilasi, yang merupakan persyaratan untuk nilai default dalam argumen opsional.

Namun, Anda dapat memberikan efek yang sama dengan membuat metode kelebihan beban daripada mencoba menggunakan parameter default:

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

Task<x> DoStuff(....)
{
    return DoStuff(...., CancellationToken.None);
}
Reed Copsey
sumber
6
Ini adalah cara yang disarankan untuk menangani hal ini, seperti yang dijelaskan pada dokumentasi Pola Asinkron Berbasis Tugas di MSDN (khususnya di bagian Memilih Beban Berlebih untuk Disediakan ).
Sam Harwell
CancellationToken.None == default benar
eoleary
6
Ada apa dengan CancellationToken cancellationToken = default(CancellationToken)? Juga dijelaskan di sini blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/…
Ray
20

Berikut adalah beberapa solusi, dalam urutan menurun dari kebaikan umum:

1. Menggunakan default(CancellationToken)sebagai nilai default:

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

Secara semantik, CancellationToken.Noneakan menjadi kandidat ideal untuk default, tetapi tidak dapat digunakan seperti itu karena ini bukan konstanta waktu kompilasi. default(CancellationToken)adalah hal terbaik berikutnya karena merupakan konstanta waktu kompilasi dan didokumentasikan secara resmi sebagai padanannyaCancellationToken.None .

2. Menyediakan metode overload tanpa CancellationTokenparameter:

Atau, jika Anda lebih suka metode kelebihan beban daripada parameter opsional (lihat ini dan pertanyaan ini tentang topik itu):

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

Untuk metode antarmuka, hal yang sama dapat dicapai dengan menggunakan metode ekstensi:

interface IFoo
{
    Task DoAsync(CancellationToken ct);
}

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

Hal ini menghasilkan antarmuka yang lebih ramping dan mencegah pelaksana dari secara eksplisit menulis kelebihan metode penerusan.

3. Membuat parameter menjadi null dan menggunakan nullsebagai nilai default:

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

Saya suka solusi ini paling tidak karena jenis nullable datang dengan overhead runtime kecil, dan referensi ke token pembatalan menjadi lebih bertele-tele karena operator penggabungan null ??.

stakx - tidak lagi berkontribusi
sumber
11

Opsi lainnya adalah menggunakan Nullable<CancellationToken>parameter, default ke null, dan tangani di dalam metode:

Task<x> DoStuff(...., CancellationToken? ct = null) {
    var token = ct ?? CancellationToken.None;
    ...
}
Todd Menier
sumber
cemerlang! sejauh ini ini adalah jawaban terbaik
John Henckel
6

Versi C # yang lebih baru memungkinkan sintaks yang disederhanakan untuk versi default (CancellationToken). Misalnya:

Task<x> DoStuff(...., CancellationToken ct = default)
Alexei
sumber
-1

Jawaban Tofutim adalah satu cara, tetapi dari komentar saya melihat bahwa orang-orang memiliki masalah dengan itu.

Dalam hal ini, saya menyarankan agar seseorang dapat memiliki metode sebagai berikut:

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

dan membebani sebagai:

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

Ini mengkompilasi, karena nilai CancellationToken.Nonetidak diperlukan pada waktu kompilasi.

Ozair Kafray
sumber