Apa kelemahan memetakan pengidentifikasi integral untuk enum?


16

Saya telah berpikir tentang membuat jenis khusus untuk pengidentifikasi seperti ini:

public enum CustomerId : int { /* intentionally empty */ }
public enum OrderId : int { }
public enum ProductId : int { }

Motivasi utama saya untuk ini adalah untuk mencegah jenis bug di mana Anda secara tidak sengaja mengirimkan orderItemId ke fungsi yang mengharapkan orderItemDetailId.

Tampaknya enum bekerja mulus dengan semua yang ingin saya gunakan dalam aplikasi web .NET yang khas:

  • Routing MVC bekerja dengan baik
  • Serialisasi JSON berfungsi dengan baik
  • Setiap ORM saya bisa memikirkan berfungsi dengan baik

Jadi sekarang saya bertanya-tanya, "mengapa saya tidak melakukan ini?" Ini adalah satu-satunya kekurangan yang dapat saya pikirkan:

  • Ini dapat membingungkan pengembang lain
  • Ini memperkenalkan inkonsistensi dalam sistem Anda jika Anda memiliki pengidentifikasi non-integral.
  • Mungkin memerlukan casting tambahan, seperti (CustomerId)42. Tapi saya tidak berpikir ini akan menjadi masalah, karena routing ORM dan MVC biasanya akan memberikan Anda nilai-nilai dari tipe enum secara langsung.

Jadi pertanyaan saya adalah, apa yang saya lewatkan? Ini mungkin ide yang buruk, tetapi mengapa?



3
Ini disebut "microtyping", Anda dapat berkeliling di Google (mis. Ini untuk Java, detailnya berbeda, tapi motivasinya sama)
qbd

Saya telah mengetik dua jawaban parsial dan kemudian menghapusnya karena saya menyadari saya memikirkan hal yang salah ini. Saya setuju dengan Anda bahwa "Ini mungkin ide yang buruk, tetapi mengapa?"
user2023861

Jawaban:


7

Merupakan ide bagus untuk membuat tipe untuk setiap jenis pengidentifikasi, terutama untuk alasan yang Anda nyatakan. Saya tidak akan melakukannya dengan mengimplementasikan tipe enum karena menjadikannya struct atau kelas yang tepat memberi Anda lebih banyak opsi:

  • Anda dapat menambahkan konversi implisit ke int. Ketika Anda memikirkannya, itu adalah arah lain, int -> CustomerId, yaitu yang bermasalah yang perlu konfirmasi eksplisit dari konsumen.
  • Anda dapat menambahkan aturan bisnis yang menentukan pengidentifikasi apa yang dapat diterima dan apa yang tidak. Ini bukan hanya tentang pemeriksaan lemah apakah intnya positif: kadang-kadang, daftar semua ID yang mungkin disimpan dalam database, tetapi perubahan hanya selama penerapan. Kemudian Anda dapat dengan mudah memeriksa bahwa ID yang diberikan ada terhadap hasil cache dari kueri yang tepat. Dengan cara ini, Anda dapat menjamin bahwa aplikasi Anda akan gagal dengan cepat di hadapan data yang tidak valid.
  • Metode pada pengidentifikasi dapat membantu Anda dengan melakukan pemeriksaan keberadaan DB yang mahal atau mendapatkan entitas / objek domain yang sesuai.

Anda dapat membaca tentang penerapannya di artikel Fungsional C #: Obsesi primitif .


1
Saya akan mengatakan Anda hampir pasti jadi tidak ingin membungkus setiap int di kelas tetapi dalam sebuah struct
jk.

Komentar yang bagus, saya memperbarui sedikit jawabannya.
Lukáš Lánský

3

Ini adalah hack yang cerdik, tetapi masih merupakan hack yang menyalahgunakan fitur untuk sesuatu yang bukan tujuan yang dimaksudkan. Hacks memiliki biaya dalam pemeliharaan, jadi itu tidak cukup Anda tidak dapat menemukan kekurangan lain, itu juga perlu memiliki manfaat yang signifikan dibandingkan dengan cara yang lebih idiomatis untuk menyelesaikan masalah yang sama.

Cara idiomatis adalah dengan membuat tipe pembungkus atau struct dengan satu bidang. Ini akan memberi Anda keamanan jenis yang sama dengan cara yang lebih eksplisit dan idiomatik. Meskipun proposal Anda memang pandai, saya tidak melihat keuntungan signifikan apa pun dalam menggunakan jenis pembungkus.


1
Keuntungan utama dari enum adalah bahwa ia "hanya berfungsi" seperti yang Anda inginkan dengan MVC, serialisasi, ORM, dll. Saya telah membuat jenis kustom (struct dan kelas) di masa lalu dan Anda akhirnya menulis lebih banyak kode daripada Anda harus dilakukan agar kerangka / pustaka tersebut berfungsi dengan tipe khusus Anda.
default.kramer

Serialisasi harus bekerja secara transparan, tetapi untuk misalnya. ORM Anda perlu mengonfigurasi beberapa jenis konversi eksplisit. Tapi ini adalah harga dari bahasa yang sangat diketik.
JacquesB

2

Dari POV saya, idenya bagus. Niat untuk memberikan tipe yang berbeda untuk kelas pengidentifikasi yang tidak terkait, dan mengekspresikan semantik melalui tipe dan membiarkan kompiler memeriksanya, adalah yang baik.

Saya tidak tahu cara kerjanya di. NET kinerja-bijaksana, misalnya adalah nilai-nilai enum tentu kotak. Kode OTOH yang bekerja dengan basis data kemungkinan I / O-terikat pula dan pengidentifikasi kotak tidak akan menjadi hambatan.

Di dunia yang sempurna, pengidentifikasi tidak akan berubah dan hanya dapat dibandingkan untuk kesetaraan; byte aktual akan menjadi detail implementasi. Pengidentifikasi yang tidak terkait akan memiliki tipe yang tidak kompatibel sehingga Anda tidak dapat menggunakan satu untuk yang lain secara tidak sengaja. Solusi Anda praktis sesuai dengan tagihan.


-1

Anda tidak dapat melakukan ini, enum harus ditentukan sebelumnya. Jadi, kecuali jika Anda bersedia menentukan sebelumnya seluruh spektrum nilai yang mungkin dari id pelanggan, jenis Anda tidak akan berguna.

Jika Anda memilih, Anda akan menentang tujuan utama Anda untuk mencegah untuk secara tidak sengaja menetapkan id tipe A ke id tipe B. Jadi enum tidak membantu tujuan Anda.

Ini memunculkan pertanyaan, apakah akan membantu untuk membuat tipe Anda untuk id pelanggan, id pesanan dan id produk dengan turun dari Int32 (atau Guid). Saya bisa memikirkan alasan mengapa ini salah.

Tujuan dari id adalah identifikasi. Tipe integral sudah sempurna untuk ini, mereka tidak mendapatkan identitas lagi dengan turun darinya. Tidak ada spesialisasi yang diperlukan atau diinginkan, dunia Anda tidak akan terwakili dengan lebih baik dengan memiliki jenis pengenal yang berbeda. Jadi dari perspektif OO itu akan buruk.

Dengan saran Anda, Anda menginginkan lebih dari keamanan jenis, Anda menginginkan keamanan nilai dan yang berada di luar domain pemodelan, itu adalah masalah operasional.


1
Tidak, enum tidak harus ditentukan sebelumnya dalam .NET. Dan intinya adalah bahwa casting hampir tidak pernah terjadi, misalnya dalam public ActionResult GetCustomer(CustomerId custId)casting tidak diperlukan - kerangka kerja MVC akan memberikan nilai.
default.kramer

2
Saya tidak setuju. Bagi saya, itu masuk akal dari perspektif OO. Int32 tidak memiliki semantik, ini murni tipe "teknis". Tapi saya memiliki kebutuhan untuk tipe Pengidentifikasi abstrak yang melayani tujuan mengidentifikasi objek dan yang kebetulan diimplementasikan sebagai Int32 (tapi bisa lama atau apa pun, tidak masalah). Kami memiliki spesialisasi konkret seperti ProductId yang berasal dari Identifier yang mengidentifikasi contoh konkret dari ProductId. Tidak masuk akal secara logis untuk memberikan nilai dari OrderItemId ke ProductId (ini jelas sebuah kesalahan) sehingga bagus bahwa sistem tipe dapat melindungi kita dari ini.
qbd

1
Saya tidak tahu C # apakah ini fleksibel mengenai penggunaan enum. Jelas membingungkan bagi saya sebagai pengembang yang terbiasa dengan fakta (?) Bahwa enum adalah enumerasi dari nilai yang mungkin. Mereka biasanya menentukan rentang id yang dinamai. Jadi sekarang tipe ini "disalahgunakan" dalam arti tidak ada rentang dan tidak ada nama, hanya tipe yang tersisa. Dan ternyata, tipenya juga tidak aman. Hal lain: contohnya jelas merupakan aplikasi basis data dan pada beberapa titik "enum" Anda akan dipetakan ke bidang tipe integral di mana saat itu Anda akan kehilangan jenis yang dianggap aman dengan aman.
Martin Maat
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.