Apa itu penyalahgunaan obat generik?

35

Saat meninjau beberapa kode, saya perhatikan kesempatan untuk mengubahnya menggunakan generik. Kode (dikaburkan) terlihat seperti:

public void DoAllTheThings(Type typeOfTarget, object[] possibleTargets)
{
    var someProperty = typeOfTarget.GetProperty(possibleTargets[0]);
    ...
}

Kode ini dapat diganti oleh obat generik, seperti:

public void DoAllTheThings<T>(object[] possibleTargets[0])
{
    var someProperty = type(T).getProperty(possibleTargets[0]);
    ...
}

Dalam meneliti manfaat dan kekurangan dari pendekatan ini saya menemukan istilah yang disebut penyalahgunaan generik . Lihat:

Pertanyaan saya ada dua bagian:

  1. Adakah manfaat untuk beralih ke obat generik seperti ini? (Kinerja? Keterbacaan?)
  2. Apa itu Penyalahgunaan Generik? Dan apakah menggunakan generik setiap kali ada parameter tipe penyalahgunaan ?
Sintaks Aturan
sumber
2
Saya bingung. Anda tahu nama properti sebelumnya tetapi bukan jenis objek sehingga Anda menggunakan refleksi untuk mendapatkan nilai?
MetaFight
3
@MetaFight Saya tahu itu contoh yang berbelit-belit, kode sebenarnya akan lama dan sulit untuk diletakkan di sini. Apapun, tujuan pertanyaan saya adalah untuk menentukan apakah mengganti parameter Type dengan generik dianggap sebagai bentuk yang baik atau buruk.
SyntaxRules
12
Saya selalu menganggap itu sebagai alasan khas mengapa obat generik ada (kata enak tidak dimaksudkan). Mengapa mengetik tipu sendiri ketika Anda dapat memanfaatkan sistem tipe Anda untuk melakukannya untuk Anda?
MetaFight
2
contoh Anda aneh, tapi saya punya perasaan persis seperti apa yang dilakukan banyak perpustakaan IoD
Ewan
14
Bukan jawaban, tetapi perlu dicatat bahwa contoh kedua kurang fleksibel daripada yang pertama. Membuat fungsi generik menghilangkan kemampuan Anda untuk lulus dalam tipe runtime; Generik perlu mengetahui jenisnya pada waktu kompilasi.
KChaloux

Jawaban:

87

Ketika obat generik diterapkan secara tepat, mereka menghapus kode alih-alih hanya mengatur ulangnya. Terutama, kode yang paling baik dihilangkan oleh generik adalah typecasts, reflection, dan dynamic typing. Oleh karena itu penyalahgunaan generik dapat secara longgar didefinisikan sebagai membuat kode generik tanpa pengurangan signifikan dalam pengetikan, refleksi, atau pengetikan dinamis dibandingkan dengan implementasi non-generik.

Sehubungan dengan contoh Anda, saya berharap penggunaan obat generik yang tepat untuk mengubah object[]ke T[]atau serupa, dan menghindari Typeatau typesama sekali. Itu mungkin memerlukan refactoring yang signifikan di tempat lain, tetapi jika menggunakan obat generik sesuai dalam kasus ini, itu akan berakhir secara keseluruhan lebih sederhana ketika Anda selesai.

Karl Bielefeldt
sumber
3
Itu poin yang bagus. Saya akan mengakui kode yang membuat saya berpikir ini melakukan beberapa refleksi yang sulit dibaca. Aku akan melihat apakah saya dapat mengubah object[]ke T[].
SyntaxRules
3
Saya suka karakter hapus vs. atur ulang.
copper.hat
13
Anda melewatkan satu kunci penggunaan obat generik - yaitu untuk mengurangi duplikasi. (Meskipun Anda dapat mengurangi duplikasi dengan memperkenalkan dosa-dosa lain yang Anda sebutkan). Jika mengurangi duplikasi tanpa menghapus typecast, refleksi, atau mengetik dinamis IMO tidak apa-apa.
Michael Anderson
4
Jawaban yang sangat bagus. Saya akan menambahkan bahwa Dalam kasus khusus ini, OP mungkin perlu beralih ke antarmuka alih-alih obat generik. Tampaknya seolah-olah refleksi sedang digunakan untuk mengakses properti tertentu dari objek; sebuah antarmuka jauh lebih cocok untuk memungkinkan kompiler menentukan bahwa properti tersebut benar-benar ada.
jpmc26
4
@MichaelAnderson "hapus kode alih-alih hanya menata ulang itu" termasuk mengurangi duplikasi
Caleth
5

Saya akan menggunakan aturan yang tidak masuk akal: Generik, seperti semua konstruksi pemrograman lainnya, ada untuk menyelesaikan masalah . Jika tidak ada masalah untuk dipecahkan untuk obat generik, menggunakannya adalah penyalahgunaan.

Dalam kasus spesifik generik, sebagian besar ada untuk abstrak dari tipe konkret, yang memungkinkan implementasi kode untuk tipe berbeda dilipat menjadi satu templat generik (atau apa pun bahasa Anda menyebutnya). Sekarang, misalkan Anda memiliki kode yang menggunakan tipe Foo. Anda mungkin mengganti jenis itu dengan generik T, tetapi jika Anda hanya membutuhkan kode itu untuk bekerja dengannya Foo, tidak ada kode lain yang dapat Anda gunakan untuk melipatnya. Dengan demikian, tidak ada masalah yang harus dipecahkan, sehingga tipuan menambahkan generik hanya akan berfungsi untuk mengurangi keterbacaan.

Akibatnya, saya sarankan untuk hanya menulis kode tanpa menggunakan obat generik, sampai Anda melihat kebutuhan untuk memperkenalkannya (karena Anda memerlukan instantiasi kedua). Kemudian, dan hanya saat itu, adalah waktu untuk memperbaiki kode untuk menggunakan obat generik. Setiap penggunaan sebelum titik itu adalah penyalahgunaan di mataku.


Ini kedengarannya agak terlalu berlebihan, jadi izinkan saya mengingatkan Anda:
Tidak ada aturan tanpa pengecualian dalam pemrograman. Aturan ini termasuk.

cmaster
sumber
Pikiran yang menarik. Namun sedikit penulisan ulang kriteria Anda dapat membantu. Misalkan OP membutuhkan "komponen" baru yang memetakan int ke string dan sebaliknya. Sangat jelas bahwa itu memecahkan kebutuhan bersama dan dapat dengan mudah digunakan kembali di masa depan jika dibuat generik. Kasus ini akan jatuh dalam definisi Anda tentang penyalahgunaan obat generik. Namun, setelah kebutuhan mendasar yang sangat umum diidentifikasi, bukankah layak untuk menginvestasikan upaya tambahan yang tidak signifikan dalam desain tanpa ini menjadi penyalahgunaan?
Christophe
@ Christophe Itu memang pertanyaan yang sulit. Ya, ada beberapa situasi di mana memang lebih baik mengabaikan aturan saya (tidak ada aturan tanpa pengecualian dalam pemrograman!). Namun, kasus konversi string spesifik sebenarnya agak terlibat: 1. Anda perlu memperlakukan tipe integer yang ditandatangani dan tidak ditandatangani secara terpisah. 2. Anda perlu memperlakukan konversi mengambang terpisah dari konversi integer. 3. Anda dapat menggunakan kembali implementasi untuk tipe integer terbesar yang tersedia untuk tipe yang lebih kecil. Anda mungkin ingin secara proaktif menulis pembungkus generik untuk melakukan casting, tetapi intinya akan tanpa generik.
cmaster
1
Saya akan berdebat menentang proaktif menulis generik (pra-gunakan daripada menggunakan kembali) sebagai kemungkinan YAGNI. Ketika Anda membutuhkannya, maka refactor.
Neil_UK
Setelah menulis pertanyaan ini, saya lebih banyak mengambil sikap "Menuliskannya sebagai obat generik jika memungkinkan untuk menghemat penggunaan kembali di masa depan." Saya tahu itu agak ekstrem. Sangat menyegarkan melihat sudut pandang Anda di sini. Terima kasih!
SyntaxRules
0

Kode itu terlihat aneh. Tetapi jika kita menyebutnya, terlihat lebih baik untuk menentukan jenisnya sebagai generik.

https://stackoverflow.com/questions/10955579/passing-just-a-type-as-a-parameter-in-c-sharp

Secara pribadi saya tidak suka contortions ini yang membuat kode panggilan terlihat cantik. misalnya seluruh hal yang 'lancar' dan Metode Penyuluhan.

Tetapi Anda harus mengakui bahwa ia memiliki pengikut yang populer. bahkan microsoft menggunakannya dalam kesatuan misalnya

container.RegisterType<IInterface,Concrete>()
Ewan
sumber
0

Bagi saya sepertinya Anda berada di jalan yang benar di sini, tetapi tidak menyelesaikan pekerjaan.

Sudahkah Anda mempertimbangkan untuk mengubah object[]parameter Func<T,object>[]untuk langkah selanjutnya?

sq33G
sumber