Apakah jenis yang dapat dibatalkan lebih disukai daripada angka ajaib?


22

Saya telah melakukan sedikit perdebatan dengan rekan kerja belakangan ini. Kami secara khusus menggunakan C #, tetapi ini bisa berlaku untuk bahasa apa pun dengan tipe nullable. Katakan misalnya Anda memiliki nilai yang mewakili maksimum. Namun, nilai maksimum ini bersifat opsional. Saya berpendapat bahwa angka yang dapat dibatalkan lebih disukai. Rekan kerja saya lebih suka menggunakan nol, dengan mengutip preseden. Memang, hal-hal seperti soket jaringan sering menggunakan nol untuk mewakili batas waktu tanpa batas. Jika saya menulis kode yang berhubungan dengan soket hari ini, saya pribadi akan menggunakan nilai nullable, karena saya merasa akan lebih baik mewakili kenyataan bahwa tidak ada batas waktu.

Representasi mana yang lebih baik? Keduanya memerlukan kondisi yang memeriksa nilai yang berarti "tidak ada", tapi saya percaya bahwa tipe nullable menyampaikan maksud sedikit lebih baik.


6
Jika angka digunakan, masukkan dalam konstanta, bukan langsung dalam kode.
Renato Dinhani

@ RenatoDinhaniConceição yang tidak bisa menjadi aturan umum. Kalau tidak, Anda berakhir softcoding semuanya.
Simon Bergot

Jawaban:


24

Mempertimbangkan:

  • Bahasa,

  • Kerangka,

  • Konteks.

1. Bahasa

Menggunakan ∞ bisa menjadi solusi maksimal.

  • JavaScript, misalnya, memiliki jumlah tak terbatas. C # tidak¹.

  • Ada, misalnya, memiliki rentang. C # tidak.

Di C #, ada int.MaxValue, tetapi Anda tidak dapat menggunakannya dalam kasing Anda. int.MaxValueadalah bilangan bulat maksimum, 2.147.483.647. Jika dalam kode Anda, Anda memiliki nilai maksimum sesuatu, seperti tekanan maksimum yang diterima sebelum sesuatu meledak, menggunakan 2.147.483.647 tidak masuk akal.

2. Kerangka Kerja

.NET Framework agak tidak konsisten dalam hal ini, dan penggunaan nilai-nilai ajaibnya dapat dikritik.

Misalnya, "Hello".IndexOf("Z")mengembalikan nilai ajaib -1. Ini mungkin membuat lebih mudah (bukan?) Untuk memanipulasi hasilnya:

int position = "Hello".IndexOf("Z");
if (position > 0)
{
    DoSomething(position);
}

daripada menggunakan struktur khusus:

SearchOccurrence occurrence = "Hello".IndexOf("Z");
if (occurrence.IsFound)
{
    DoSomething(occurrence.StartOffset);
}

tetapi tidak intuitif sama sekali. Kenapa -1dan tidak -123? Seorang pemula juga mungkin secara keliru berpikir bahwa itu 0berarti "Tidak ditemukan" juga atau salah ketik (position >= 0).

3. Konteks

Jika kode Anda terkait dengan timeout di soket jaringan, menggunakan sesuatu yang telah digunakan oleh semua orang selama beberapa dekade demi menjadi konsisten bukanlah ide yang buruk . Terutama, 0untuk batas waktu sangat jelas: ini adalah nilai yang tidak boleh nol. Menggunakan kelas khusus dalam hal ini dapat membuat hal-hal lebih sulit untuk dipahami:

class Timeout
{
    // A value indicating whether there is a timeout.
    public bool IsTimeoutEnabled { get; set; }

    // The duration of the timeout, in milliseconds.
    public int Duration { get; set; }
}
  • Dapatkah saya menetapkan Durationke 0 jika IsTimeoutEnableditu benar?
  • Jika IsTimeoutEnabledsalah, apa yang terjadi jika saya menetapkan Durationke 100?

Ini dapat menyebabkan banyak kesalahan. Bayangkan potongan kode berikut:

this.currentOperation.Timeout = new Timeout
{
    // Set the timeout to 200 ms.; we don't want this operation to be longer than that.
    Duration = 200,
};

this.currentOperation.Run();

Operasi berjalan selama sepuluh detik. Bisakah Anda melihat apa yang salah dengan kode ini, tanpa membaca dokumentasi Timeoutkelas?

Kesimpulan

  • nullmengungkapkan dengan baik gagasan bahwa nilainya tidak ada di sini. Itu tidak disediakan. Tidak tersedia. Ini bukan angka, atau string nol / kosong atau apa pun. Jangan gunakan untuk nilai maksimum atau minimum.

  • int.MaxValuesangat terkait dengan bahasa itu sendiri. Jangan gunakan int.MaxValueuntuk batas kecepatan maksimum Vehiclekelas atau kecepatan maksimum yang dapat diterima untuk pesawat terbang, dll.

  • Hindari nilai-nilai ajaib seperti -1dalam kode Anda. Mereka menyesatkan dan menyebabkan kesalahan dalam kode.

  • Buat kelas Anda sendiri yang akan lebih mudah, dengan nilai minimum / maksimum yang ditentukan. Misalnya VehicleSpeedbisa punya VehicleSpeed.MaxValue.

  • Jangan ikuti panduan sebelumnya dan gunakan nilai sihir jika itu adalah konvensi umum selama beberapa dekade dalam bidang yang sangat spesifik, yang digunakan oleh kebanyakan orang menulis kode di bidang ini.

  • Jangan lupa untuk menggabungkan pendekatan. Sebagai contoh:

    class DnsQuery
    {
        public const int NoTimeout = 0;
    
        public int Timeout { get; set; }
    }
    
    this.query.Timeout = 0; // For people who are familiar with timeouts set to zero.
    // or
    this.query.Timeout = DnsQuery.NoTimeout; // For other people.
    

¹ Anda dapat membuat tipe Anda sendiri yang mencakup infinity. Di sini, saya berbicara tentang inttipe asli saja.


1
"Menggunakan sesuatu yang digunakan oleh semua orang selama beberapa dekade demi menjadi konsisten bukanlah ide yang buruk" / "Jangan ikuti pedoman sebelumnya dan gunakan nilai-nilai sihir jika itu adalah konvensi umum selama beberapa dekade dalam bidang yang sangat spesifik, yang digunakan oleh kebanyakan orang menulis kode di bidang ini. " - Ada kesalahan ketik di suatu tempat, saya pikir?
deworde

1
@deworde Saya yakin MainMa merujuk pada pedoman yang dia berikan di atas yang itu.
Joshua Drake

1
Saya tidak setuju pada contoh indexOf, karena -1 berada di luar string, yang pasti adalah Z.
Joshua Drake

5
"JavaScript, misalnya, memiliki infinity. C # tidak." - ya
BlueRaja - Danny Pflughoeft

+1 khusus untuk "Buat kelas Anda sendiri" yang akan saya sarankan. Setiap kali telanjang inttidak cukup mengungkapkan tentang jenis untuk membatasi masalah, pertimbangkan struct baru dengan info lebih lanjut (contoh konst dari struct yang mewakili nilai-nilai sihir, misalnya, atau enum untuk mengindikasikan). Atau pertimbangkan pemrograman kontrak atau beberapa solusi lain, tapi saya pikir struct kustom paling mudah.
CodexArcanum

12

Null tidak lebih baik dari angka ajaib.

Yang penting adalah untuk NAMA nilai-nilai yang memiliki efek sihir, jika Anda harus memiliki nilai-nilai seperti itu, dan untuk memastikan bahwa definisi nama-nama itu adalah suatu tempat yang akan dilihat oleh siapa saja yang menabrak nilai sihir dan wtf.

if (timeout == 4298435) ... // bad.
if (timeout == null) ... // bad.
if (timeout == NEVER_TIME_OUT) ... // yay! puppies and unicorns!

2
Ok, mungkin itu memang lebih tergantung pada bahasa, tetapi dalam C #, Anda mungkin akan melakukan: if (timeout.HasValue) alih-alih perbandingan langsung ke nol.
Matt H

2
Null tidak lebih buruk dari angka ajaib. Dengan angka ajaib, Anda tidak pernah tahu angka ajaib itu ... bisa 0, -1, atau yang lainnya. null hanya null.
marco-fiset

9
Null berarti tidak adanya nilai. Ini adalah konsep yang ingin diungkapkan oleh banyak angka ajaib. Mampu menggunakan null dengan tipe nullable adalah solusi yang JAUH lebih baik daripada memilih satu nilai arbitrer dari rentang nilai yang mungkin untuk tipe data.
17 dari 26

2
Menggunakan "null" sebagai nilai ajaib Anda, jika tipe Anda memiliki "null," tidak apa-apa. Yang penting adalah untuk NAMA itu, karena tentu saja menembak orang yang akan datang tidak akan tahu apa yang Anda maksud. Null dapat berarti "tak terhingga," "belum ditentukan," "bug dalam kode yang membuat struktur data," atau sejumlah hal lainnya. Hanya nama yang memberi tahu pembuat kode berikutnya bahwa Anda bermaksud memberikan nilai itu, dan perilaku apa yang Anda maksudkan untuk memicu.
mjfgates

1
@ CodeInChaos: Saya tahu Anda bisa melakukan keduanya, tapi saya lebih suka HasValue. Saya sebenarnya bukan penggemar berat null secara umum, tetapi tipe nullable yang menggunakan HasValue terasa sedikit lebih dekat dengan Opsi / Mungkin tipe bagi saya, yang saya penggemar.
Matt H

10

MAGIC_NUMBERkode harus benar-benar selalu dihindari sedapat mungkin. nulladalah ungkapan niat yang jauh lebih jelas.


6

Dalam C #, banyak kelas CLR memiliki anggota statis Empty:

  • System.String.Empty
  • System.EventArgs.Empty
  • System.Guid.Empty
  • System.Drawing.Rectangle.Empty
  • System.Windows.Size.Empty

Ini membuat Anda tidak perlu mengingat apakah akan menggunakan nilai ajaib atau menggunakan null untuk membangun objek kosong.

Tetapi bagaimana jika Anda berurusan dengan tipe nilai sederhana seperti int? Dalam hal itu, pertimbangkan apakah Anda menjadi korban Obsesi Primitif . Sangat mungkin bahwa properti numerik Anda yang tampaknya sederhana akan mendapat manfaat dari kelas atau struct sendiri, yang akan memungkinkan Anda untuk menentukan Emptyanggota dan juga menambahkan perilaku lain yang spesifik untuk nilai semacam itu.


3

Dalam hal ini, nilai nol adalah cara yang bagus untuk menunjukkan tidak ada maksimum. Secara umum ketika kasus khusus berarti bahwa nilai yang dipermasalahkan tidak berlaku, bahwa Anda tidak ingin fitur yang dikonfigurasikan, null adalah indikasi yang baik untuk ini.

Masalah dengan menggunakan null untuk mewakili kasus khusus adalah bahwa hanya ada satu nilai nol, dan mungkin ada beberapa kasus khusus. Dalam hal ini, saya akan meneruskan enumerasi sebagai parameter tambahan, yang dapat menunjukkan kasus khusus, atau menggunakan nilai int secara normal. (Ini pada dasarnya apa yang Nullable <> lakukan untuk Anda, meskipun ia menggunakan boolean bukan enum dan menggabungkan parameter ke dalam satu struktur.)


3

Dalam hal ini, saya pikir tipe nullable masuk akal.

Null berarti tidak adanya nilai. Ini adalah konsep yang sangat berbeda dari angka yang memiliki nilai 0.

Jika Anda ingin mengatakan "Jika saya tidak memberi Anda nilai, gunakan nilai maksimum" lalu memasukkan nol adalah cara yang tepat untuk mengekspresikannya.


1

Null: nilai kesalahan umum, tidak ditentukan, tidak berlaku, atau tidak ada nilai.

Nol: Nilai aktual, tetapi tidak selalu logis atau intuitif (dalam konteks ini). Juga merupakan nilai umum dalam inisialisasi.

Dalam konteks masalah Anda, timeoutInMillisecondsproperti bersifat opsional dan tidak disebutkan bahwa overhead pendekatan ini akan mendiskualifikasi sebagai opsi.

Kesimpulan: Ada pengecualian, dan solusi bervariasi berdasarkan bahasa dan domain; dalam hal ini, saya akan memilih Null. Di mana (saya percaya) beberapa orang melakukan kesalahan ini adalah ketika mereka tidak memisahkan data dari antarmuka dengan baik. Mereka hanya mengharapkan klien untuk membaca dokumentasi (atau implementasi) untuk menentukan bagaimana nilai-nilai khusus ini akan digunakan / ditangani - kasus-kasus khusus bocor ke dalam program klien dan itu bisa sangat tidak jelas. Dengan menambahkan lapisan abstraksi yang baik, penggunaannya bisa jauh lebih jelas.


0

Null lebih buruk untuk digunakan daripada MagicNumber. Null mewakili ide yang diekspresikan lebih baik, tetapi tidak konsisten di seluruh platform dalam bagaimana berperilaku, menggunakan MagicNumberselalu bekerja sama yang bermanfaat.

tergantung pada lingkungan / bahasa yang digunakan null bisa

  • cukup 0
  • mungkin bukan nilai hukum
  • dapat menghasilkan hasil yang tidak terduga karena logika tiga arah

MagicNumber selalu berperilaku sama.


0

Jika Anda lupa untuk memeriksa nomor ajaib (yang akan terjadi dengan benar), maka nomor ajaib akan berlanjut sebentar dengan data yang tidak masuk akal. Jauh lebih baik memiliki null yang menyebabkan pengecualian sesegera mungkin.


-1

Null bukan satu-satunya alternatif untuk angka ajaib.

public static int NO_TIMEOUT = 0;  // javaish

Null itu jahat. Dalam contoh di atas, Anda mungkin bisa lolos karena kode jelas akan dapat menangani nol. Tetapi secara umum apa yang terjadi ketika Anda mulai melewati nol adalah bahwa cepat atau lambat Anda mendapatkan pengecualian penunjuk nol. Ini mungkin tidak terjadi ketika Anda pertama kali menulis kode, tetapi kode dipertahankan lebih lama dari rilis pertama. Ini sering dikelola oleh orang-orang yang tidak tahu banyak tentang sistem seperti pengembang asli.

Scala (misalnya) memiliki alternatif yang bagus di kelas Opsi. Kelas Opsi memiliki satu dari dua nilai, Beberapa - yang membungkus nilai yang Anda inginkan dan Tidak Ada - yang tidak memiliki nilai.

Itu membuat jelas bagi pengembang mana pun bahwa mungkin tidak ada nilai dan Anda memiliki kode yang lebih baik untuk itu. Yah, bagaimanapun juga harus jelas.

Dan tidak semua angka ajaib adalah masalah. Tergantung pada konteks 0, 1, 1024 dll semuanya bisa jelas. 347? Ya, yang harus Anda hindari. :-)


4
-1: Membenarkan "null is evil".
deworde

4
Menentukan alias untuk suatu angka tidak mengubah fakta bahwa itu masih berupa angka ajaib.
17 dari 26

Yah, mungkin Anda memiliki definisi angka ajaib yang berbeda dari saya. Silakan lihat en.wikipedia.org/wiki/…
Jon Strayer

1
Saya setuju dengan Jon Strayer di sini. Null adalah contoh ADT dalam bahasa yang tidak benar-benar mendukung ADT. OP mungkin bisa lolos begitu saja di sini, tetapi secara umum saya menganggap bahasa apa pun yang gagal sedikit pun gagal pada pemrogramnya.
Jeremy Wall
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.