Keduanya menghasilkan kesalahan dengan mengatakan bahwa mereka harus berupa konstanta waktu kompilasi:
void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))
Pertama-tama, bisakah seseorang menjelaskan mengapa nilai-nilai ini tidak dapat ditentukan pada waktu kompilasi? Dan apakah ada cara untuk menentukan nilai default untuk objek TimeSpan opsional?
c#
c#-4.0
default-value
timespan
optional-parameters
Mike Pateras
sumber
sumber
new TimeSpan(2000)
itu tidak berarti 2000 milidetik, itu berarti 2000 "kutu" yang merupakan 0,2 milidetik, atau satu per sepuluh dari dua detik.Jawaban:
Anda dapat mengatasi ini dengan sangat mudah dengan mengubah tanda tangan Anda.
Saya harus menguraikan - alasan ungkapan-ungkapan dalam contoh Anda tidak konstanta waktu kompilasi adalah karena pada waktu kompilasi, kompiler tidak dapat hanya menjalankan TimeSpan.FromSeconds (2.0) dan menempelkan byte hasil ke dalam kode kompilasi Anda.
Sebagai contoh, pertimbangkan jika Anda mencoba menggunakan DateTime. Sekarang sebagai gantinya. Nilai DateTime. Sekarang berubah setiap kali dieksekusi. Atau anggap TimeSpan.FromSeconds mempertimbangkan gravitasi. Ini adalah contoh yang tidak masuk akal tetapi aturan konstanta waktu kompilasi tidak membuat kasus khusus hanya karena kita mengetahui bahwa TimeSpan.
sumber
<param>
, karena tidak terlihat di tanda tangan.span = span ?? TimeSpan.FromSeconds(2.0);
dengan tipe nullable, di method body. Atauvar realSpan = span ?? TimeSpan.FromSeconds(2.0);
untuk mendapatkan variabel lokal yang tidak dapat dibatalkan.Warisan VB6 saya membuat saya tidak nyaman dengan gagasan untuk mempertimbangkan "nilai nol" dan "nilai hilang" menjadi setara. Dalam kebanyakan kasus, ini mungkin baik-baik saja, tetapi Anda mungkin memiliki efek samping yang tidak diinginkan, atau Anda mungkin menelan kondisi yang luar biasa (misalnya, jika sumbernya
span
adalah properti atau variabel yang tidak boleh nol, tetapi adalah).Karena itu saya akan membebani metode:
sumber
Ini berfungsi dengan baik:
void Foo(TimeSpan span = default(TimeSpan))
sumber
TimeSpan
nilai lain yang sewenang - wenang , seperti yang diberikan olehnew TimeSpan(2000)
.Himpunan nilai yang dapat digunakan sebagai nilai default sama dengan yang dapat digunakan untuk argumen atribut. Alasannya adalah bahwa nilai default dikodekan ke dalam metadata di dalam
DefaultParameterValueAttribute
.Mengapa tidak dapat ditentukan pada waktu kompilasi. Rangkaian nilai dan ekspresi atas nilai-nilai tersebut yang diizinkan pada waktu kompilasi tercantum dalam spesifikasi bahasa C # resmi :
Tipe
TimeSpan
ini tidak cocok dengan salah satu dari daftar ini dan karenanya tidak dapat digunakan sebagai konstanta.sumber
TimeSpan
bisa muat yang terakhir dalam daftardefault(TimeSpan)
ini valid.asalkan
default(TimeSpan)
bukan nilai yang valid untuk fungsi tersebut.Atau
asalkan
new TimeSpan()
bukan nilai yang valid.Atau
Ini harus lebih baik mengingat peluang
null
nilai sebagai nilai yang valid untuk fungsi jarang terjadi.sumber
TimeSpan
adalah kasus khusus untukDefaultValueAttribute
dan ditentukan menggunakan string apa pun yang dapat diuraikan melaluiTimeSpan.Parse
metode ini.sumber
Saran saya:
BTW
TimeSpan.FromSeconds(2.0)
tidak samanew TimeSpan(2000)
- konstruktor mengambil kutu.sumber
Jawaban lain telah memberikan penjelasan yang bagus mengapa parameter opsional tidak bisa menjadi ekspresi dinamis. Tetapi, untuk menghitung ulang, parameter default berperilaku seperti konstanta waktu kompilasi. Itu artinya kompiler harus dapat mengevaluasinya dan memberikan jawaban. Ada beberapa orang yang ingin C # menambahkan dukungan untuk kompiler yang mengevaluasi ekspresi dinamis ketika menghadapi deklarasi konstan — fitur semacam ini akan terkait dengan metode menandai "murni", tetapi itu bukan kenyataan saat ini dan mungkin tidak akan pernah terjadi.
Salah satu alternatif untuk menggunakan parameter default C # untuk metode seperti itu adalah dengan menggunakan pola yang dicontohkan oleh
XmlReaderSettings
. Dalam pola ini, tentukan kelas dengan konstruktor tanpa parameter dan properti yang dapat ditulis untuk umum. Kemudian ganti semua opsi dengan default di metode Anda dengan objek jenis ini. Bahkan membuat objek ini opsional dengan menentukan defaultnull
untuk itu. Sebagai contoh:Untuk menelepon, gunakan satu sintaks aneh itu untuk membuat instance dan menetapkan properti semua dalam satu ekspresi:
Kerugian
Ini adalah pendekatan yang sangat berat untuk menyelesaikan masalah ini. Jika Anda menulis antarmuka internal yang cepat dan kotor dan membuat
TimeSpan
nullable dan memperlakukan null seperti nilai default yang Anda inginkan akan berfungsi dengan baik, lakukan itu.Juga, jika Anda memiliki sejumlah besar parameter atau memanggil metode dalam loop ketat, ini akan memiliki overhead instantiations kelas. Tentu saja, jika memanggil metode seperti itu dalam loop ketat, itu mungkin alami dan bahkan sangat mudah untuk menggunakan kembali instance
FooSettings
objek.Manfaat
Seperti yang saya sebutkan dalam komentar di contoh, saya pikir pola ini bagus untuk API publik. Menambahkan properti baru ke kelas adalah perubahan ABI yang tidak terputus-putus, sehingga Anda dapat menambahkan parameter opsional baru tanpa mengubah tanda tangan metode Anda menggunakan pola ini — memberikan lebih banyak kode yang dikompilasi baru-baru ini lebih banyak pilihan sambil terus mendukung kode yang dikompilasi lama tanpa kerja ekstra .
Selain itu, karena parameter metode bawaan bawaan C # diperlakukan sebagai konstanta waktu-kompas dan dimasukkan ke dalam lokasi panggilan, parameter default hanya akan digunakan oleh kode setelah dikompilasi ulang. Dengan membuat instance objek pengaturan, pemanggil secara dinamis memuat nilai default saat memanggil metode Anda. Ini berarti bahwa Anda dapat memperbarui default dengan hanya mengubah kelas pengaturan Anda. Dengan demikian, pola ini memungkinkan Anda mengubah nilai default tanpa harus mengkompilasi ulang penelepon untuk melihat nilai-nilai baru, jika itu diinginkan.
sumber