Saya baru-baru ini berpikir tentang penggunaan bilangan bulat tak bertanda di C # (dan saya kira argumen serupa dapat dikatakan tentang "bahasa tingkat tinggi" lainnya)
Ketika Membutuhkan bilangan bulat saya biasanya tidak dihadapkan dengan dilema ukuran bilangan bulat, sebuah contoh akan menjadi properti usia kelas Person (tetapi pertanyaannya tidak terbatas pada properti). Dengan mengingat hal itu, sejauh yang saya bisa lihat, hanya satu keuntungan menggunakan integer yang tidak ditandatangani ("uint") dibandingkan integer yang ditandatangani ("int") - keterbacaan. Jika saya ingin mengungkapkan gagasan bahwa suatu usia hanya dapat menjadi positif, saya dapat mencapainya dengan mengatur jenis usia ke usia.
Di sisi lain, perhitungan pada bilangan bulat tak bertanda dapat menyebabkan kesalahan dalam semua jenis dan membuatnya sulit untuk melakukan operasi seperti mengurangi dua usia. (Saya membaca ini adalah salah satu alasan Java menghilangkan bilangan bulat yang tidak ditandai)
Dalam kasus C # saya juga dapat berpikir bahwa klausa penjaga pada setter akan menjadi solusi yang memberikan yang terbaik dari dua dunia, tetapi, ini tidak akan berlaku ketika saya misalnya, suatu zaman akan diberikan ke beberapa metode. Solusinya adalah dengan mendefinisikan kelas yang disebut Umur dan memiliki usia properti menjadi satu-satunya hal di sana, tetapi pola ini akan membuat saya membuat banyak kelas dan akan menjadi sumber kebingungan (pengembang lain tidak akan tahu kapan objek hanya pembungkus dan ketika itu sesuatu yang lebih sofisticaded).
Apa beberapa praktik umum terbaik mengenai masalah ini? Bagaimana saya harus menghadapi skenario seperti ini?
sumber
Jawaban:
Desainer .NET Framework memilih integer bertanda 32 bit sebagai "nomor tujuan umum" mereka karena beberapa alasan:
Alasan untuk menggunakan int unsigned bukan keterbacaan; itu memiliki kemampuan untuk mendapatkan matematika yang hanya disediakan oleh int tanpa tanda tangan.
Klausa penjaga, validasi, dan prasyarat kontrak adalah cara yang bisa diterima untuk memastikan rentang angka yang valid. Jarang rentang numerik dunia nyata sesuai dengan angka antara nol dan 2 32 -1 (atau apa pun rentang numerik asli dari tipe numerik yang Anda pilih), jadi menggunakan a
uint
untuk membatasi kontrak antarmuka Anda ke angka positif adalah jenis di samping intinya.sumber
for (uint j=some_size-1; j >= 0; --j)
- whoops ( tidak yakin apakah ini merupakan masalah dalam C #)! Saya menemukan masalah ini dalam kode sebelum yang mencoba menggunakan int unsigned di sisi C sebanyak mungkin - dan kami akhirnya mengubahnya hanya untuk mendukungint
nanti, dan hidup kami jauh lebih mudah dengan lebih sedikit peringatan kompiler juga.int
sebagian besar waktu karena itu adalah konvensi yang mapan, dan itulah yang diharapkan kebanyakan orang untuk melihat digunakan secara rutin. Gunakanuint
ketika Anda membutuhkan kapabilitas khusus auint
." Ingat, perancang Kerangka memutuskan untuk mengikuti konvensi ini secara luas, sehingga Anda bahkan tidak dapat menggunakannyauint
dalam banyak konteks Kerangka (tidak kompatibel dengan jenis).Secara umum, Anda harus selalu menggunakan tipe data yang paling spesifik untuk data Anda.
Jika, misalnya, Anda menggunakan Entity Framework untuk menarik data dari database, EF akan secara otomatis menggunakan tipe data yang paling dekat dengan yang digunakan dalam database.
Ada dua masalah dengan ini di C #.
Pertama, sebagian besar pengembang C # hanya menggunakan
int
, untuk mewakili bilangan bulat (kecuali ada alasan untuk menggunakannyalong
). Ini berarti bahwa pengembang lain tidak akan berpikir untuk memeriksa tipe data, sehingga mereka akan mendapatkan kesalahan limpahan yang disebutkan di atas. Kedua, dan isu yang lebih penting, adalah / adalah bahwa NET operator aritmatika asli hanya didukungint
,uint
,long
,ulong
,float
, ganda, dandecimal
*. Ini masih terjadi sampai sekarang (lihat bagian 7.8.4 dalam spesifikasi bahasa C # 5.0 ). Anda dapat mengujinya sendiri menggunakan kode berikut:Hasil dari
byte
-byte
adalahint
(System.Int32
).Kedua masalah ini memunculkan praktik "hanya gunakan int untuk bilangan bulat" yang sangat umum.
Jadi untuk menjawab pertanyaan Anda, dalam C # biasanya merupakan ide yang baik untuk tetap
int
kecuali:byte
dan aint
atau aint
dan along
sangat penting, atau perbedaan aritmatika dari unsigned sudah disebutkan sebelumnya).Jika Anda perlu melakukan perhitungan matematika pada data, patuhi jenis yang umum.
Ingat, Anda dapat melakukan cast dari satu tipe ke tipe lainnya. Ini bisa kurang efisien dari sudut pandang CPU, jadi Anda mungkin lebih baik dengan salah satu dari 7 tipe umum, tetapi ini merupakan opsi jika diperlukan.
Pencacahan (
enum
) adalah salah satu pengecualian pribadi saya untuk pedoman di atas. Jika saya hanya memiliki beberapa opsi, saya akan menentukan enum sebagai byte atau pendek. Jika saya membutuhkan bit terakhir dalam enum yang ditandai, saya akan menentukan jenisnyauint
sehingga saya dapat menggunakan hex untuk mengatur nilai untuk flag.Jika Anda menggunakan properti dengan kode pembatasan nilai, pastikan untuk menjelaskan dalam tag ringkasan batasan apa yang ada dan mengapa.
* Alias C # digunakan sebagai ganti nama .NET seperti
System.Int32
karena ini adalah pertanyaan C #.Catatan: ada blog atau artikel dari pengembang .NET (yang tidak dapat saya temukan), yang menunjukkan terbatasnya fungsi aritmatika dan beberapa alasan mengapa mereka tidak khawatir tentang hal itu. Seingat saya, mereka mengindikasikan bahwa mereka tidak punya rencana untuk menambahkan dukungan untuk tipe data lainnya.
Catatan: Java tidak mendukung tipe data yang tidak ditandatangani dan sebelumnya tidak memiliki dukungan untuk angka bulat 8 atau 16 bit. Karena banyak pengembang C # berasal dari latar belakang Java atau diperlukan untuk bekerja dalam kedua bahasa, keterbatasan satu bahasa terkadang secara artifisial dipaksakan pada yang lain.
sumber
Anda terutama perlu mengetahui dua hal: data yang Anda wakili, dan setiap langkah perantara dalam perhitungan Anda.
Masuk akal untuk memiliki usia
unsigned int
, karena kita biasanya tidak mempertimbangkan usia negatif. Tapi kemudian Anda menyebutkan mengurangi satu usia dari yang lain. Jika kita secara buta mengurangi satu bilangan bulat dari bilangan bulat lainnya, maka sangat mungkin untuk berakhir dengan angka negatif, bahkan jika kita sebelumnya sepakat bahwa usia negatif tidak masuk akal. Jadi dalam hal ini Anda ingin perhitungan Anda dilakukan dengan integer yang ditandatangani.Berkenaan dengan apakah nilai yang tidak ditandatangani buruk atau tidak, saya akan mengatakan bahwa itu adalah generalisasi besar untuk mengatakan nilai yang tidak ditandatangani itu buruk. Java tidak memiliki nilai yang tidak ditandatangani, seperti yang Anda sebutkan, dan itu selalu mengganggu saya. A
byte
dapat memiliki nilai dari 0-255 atau 0x00-0xFF. Tetapi jika Anda ingin instantiate byte yang lebih besar dari 127 (0x7F), Anda harus menuliskannya sebagai angka negatif atau melemparkan integer ke byte. Anda berakhir dengan kode yang terlihat seperti ini:Di atas mengganggu saya tanpa akhir. Saya tidak diizinkan memiliki byte yang memiliki nilai 197, meskipun itu adalah nilai yang sangat valid untuk kebanyakan orang waras yang berurusan dengan byte. Saya dapat menggunakan integer atau saya dapat menemukan nilai negatif (197 == -59 dalam kasus ini). Pertimbangkan juga ini:
Jadi seperti yang Anda lihat, menambahkan dua byte dengan nilai yang valid, dan berakhir dengan byte dengan nilai yang valid, akhirnya mengubah tandanya. Bukan hanya itu tetapi tidak segera jelas bahwa 70 + 80 == -106. Secara teknis ini adalah overflow, tetapi dalam pikiran saya (sebagai manusia) satu byte seharusnya tidak melebihi nilai di bawah 0xFF. Ketika saya melakukan bit aritmatika di atas kertas, saya tidak menganggap bit ke-8 sebagai bit tanda.
Saya bekerja dengan banyak bilangan bulat pada tingkat bit, dan memiliki semua yang ditandatangani biasanya membuat semuanya kurang intuitif dan lebih sulit untuk ditangani, karena Anda harus ingat bahwa menggeser angka negatif memberi Anda yang baru
1
di angka Anda. Sedangkan menggeser bilangan bulat yang tidak ditandatangani tidak pernah melakukan itu. Sebagai contoh:Itu hanya menambahkan langkah-langkah tambahan yang saya rasa tidak perlu.
Sementara saya menggunakan di
byte
atas, hal yang sama berlaku untuk bilangan bulat 32-bit dan 64-bit. Tidak memilikiunsigned
melumpuhkan dan itu mengejutkan saya bahwa ada bahasa tingkat tinggi seperti Java yang tidak memungkinkan mereka sama sekali. Tetapi bagi kebanyakan orang ini adalah masalah, karena banyak programmer tidak berurusan dengan aritmatika tingkat bit.Pada akhirnya, ini berguna untuk menggunakan bilangan bulat yang tidak ditandatangani jika Anda menganggapnya sebagai bit, dan itu berguna untuk menggunakan bilangan bulat yang ditandatangani ketika Anda menganggapnya sebagai angka.
sumber