Apakah lebih baik digunakan List<string>
dalam jenis anotasi atau di StringList
manaStringList
class StringList : List<String> { /* no further code!*/ }
c#
inheritance
Ruudjah
sumber
sumber
StringList
danList<string>
? Tidak ada, namun Anda (generik "Anda") berhasil menambahkan detail lain ke basis kode yang unik hanya untuk proyek Anda dan tidak berarti apa-apa bagi orang lain sampai setelah mereka mempelajari kode tersebut. Mengapa menyembunyikan nama daftar string hanya agar tetap menyebutnya daftar string? Di sisi lain, jika Anda ingin melakukannya,BagOBagels : List<string>
saya pikir itu lebih masuk akal. Anda dapat mengulanginya sebagai IEnumerable, konsumen eksternal tidak peduli dengan implementasinya - baiklah.Jawaban:
Ada alasan lain mengapa Anda mungkin ingin mewarisi dari tipe generik.
Microsoft merekomendasikan untuk menghindari bertelur tipe generik dalam tanda tangan metode .
Maka itu ide yang bagus, untuk membuat jenis-jenis nama bisnis-domain untuk beberapa obat generik Anda. Alih-alih memiliki
IEnumerable<IDictionary<string, MyClass>>
, buat tipeMyDictionary : IDictionary<string, MyClass>
dan kemudian anggota Anda menjadiIEnumerable<MyDictionary>
, yang jauh lebih mudah dibaca. Ini juga memungkinkan Anda untuk menggunakan MyDictionary di sejumlah tempat, meningkatkan kejelasan kode Anda.Ini mungkin kedengarannya bukan manfaat besar, tetapi dalam kode bisnis dunia nyata saya telah melihat obat generik yang akan membuat rambut Anda berdiri. Hal-hal seperti
List<Tuple<string, Dictionary<int, List<Dictionary<string, List<double>>>>>
. Arti generik itu sama sekali tidak jelas. Namun, jika dibangun dengan serangkaian jenis yang dibuat secara eksplisit, maksud pengembang asli kemungkinan akan jauh lebih jelas. Lebih jauh lagi, jika tipe generik itu perlu diubah karena suatu alasan, menemukan dan mengganti semua instance dari tipe generik itu mungkin benar-benar menyakitkan, dibandingkan dengan mengubahnya di kelas turunan.Akhirnya, ada alasan lain mengapa seseorang mungkin ingin membuat tipenya sendiri yang berasal dari obat generik. Pertimbangkan kelas-kelas berikut:
Sekarang bagaimana kita tahu mana yang
ICollection<MyItems>
harus dimasukkan ke konstruktor untuk MyConsumerClass? Jika kita membuat kelas turunanclass MyItems : ICollection<MyItems>
danclass MyItemCache : ICollection<MyItems>
, MyConsumerClass kemudian bisa sangat spesifik tentang yang mana dari dua koleksi yang sebenarnya diinginkan. Ini kemudian bekerja dengan sangat baik dengan wadah IoC, yang dapat menyelesaikan dua koleksi dengan sangat mudah. Ini juga memungkinkan programmer untuk lebih akurat - jika masuk akal untukMyConsumerClass
bekerja dengan ICollection apa pun, mereka dapat menggunakan konstruktor generik. Namun, jika tidak masuk akal secara bisnis untuk menggunakan salah satu dari dua jenis turunan, pengembang dapat membatasi parameter konstruktor ke jenis tertentu.Singkatnya, seringkali bermanfaat untuk mendapatkan tipe dari tipe generik - memungkinkan kode yang lebih eksplisit jika sesuai dan membantu keterbacaan dan pemeliharaan.
sumber
Pada prinsipnya tidak ada perbedaan antara mewarisi dari tipe generik dan mewarisi dari yang lain.
Namun, mengapa Anda akan berasal dari kelas mana saja dan tidak menambahkan apa pun lebih lanjut?
sumber
Saya tidak tahu C # dengan sangat baik, tetapi saya percaya ini adalah satu-satunya cara untuk mengimplementasikan
typedef
, yang biasanya digunakan oleh para programmer C ++ karena beberapa alasan:Mereka pada dasarnya membuang keuntungan terakhir dengan memilih nama generik yang membosankan, tetapi dua alasan lainnya masih bertahan.
sumber
StringList
adalah sebuahList<string>
- mereka dapat digunakan secara bergantian. Ini penting ketika bekerja dengan API lain (atau ketika mengekspos API Anda), karena semua orang di dunia menggunakannyaList<string>
.using StringList = List<string>;
adalah setara C # terdekat dari typedef. Subklasifikasi seperti ini akan mencegah penggunaan konstruktor dengan argumen.using
membatasi ke file kode sumber di manausing
ditempatkan. Dari sudut pandang ini, pewarisan lebih dekattypedef
. Dan membuat subclass tidak mencegah seseorang menambahkan semua konstruktor yang dibutuhkan (meskipun mungkin membosankan).using
tidak dapat digunakan untuk tujuan ini, tetapi pewarisan bisa. Ini menunjukkan mengapa saya tidak setuju dengan komentar terbalik Pete Kirkham - saya tidak menganggapusing
sebagai alternatif nyata untuk C ++typedef
.Saya dapat memikirkan setidaknya satu alasan praktis.
Mendeklarasikan tipe non-generik yang mewarisi dari tipe generik (bahkan tanpa menambahkan apa pun), memungkinkan tipe tersebut untuk dirujuk dari tempat-tempat di mana mereka tidak akan tersedia, atau tersedia dengan cara yang lebih berbelit-belit daripada seharusnya.
Salah satu kasusnya adalah ketika menambahkan pengaturan melalui editor Pengaturan di Visual Studio, di mana hanya jenis non-generik yang tampaknya tersedia. Jika Anda pergi ke sana, Anda akan melihat bahwa semua
System.Collections
kelas tersedia, tetapi tidak ada koleksi dari yangSystem.Collections.Generic
berlaku.Kasus lain adalah ketika instantiating jenis melalui XAML di WPF. Tidak ada dukungan untuk melewati argumen tipe dalam sintaks XML saat menggunakan skema pra-2009. Ini diperburuk oleh kenyataan bahwa skema 2006 tampaknya menjadi default untuk proyek WPF baru.
sumber
Saya pikir Anda perlu melihat beberapa sejarah .
Kalau tidak, Theodoros Chatzigiannakis punya jawaban terbaik, misalnya masih ada banyak alat yang tidak mengerti tipe generik. Atau mungkin bahkan campuran jawaban dan sejarahnya.
sumber
Dalam beberapa kasus tidak mungkin untuk menggunakan tipe generik, misalnya Anda tidak dapat referensi tipe generik di XAML. Dalam kasus ini, Anda dapat membuat tipe non-generik yang mewarisi dari tipe generik yang awalnya ingin Anda gunakan.
Jika memungkinkan, saya akan menghindarinya.
Tidak seperti typedef, Anda membuat tipe baru. Jika Anda menggunakan StringList sebagai parameter input ke suatu metode, penelepon Anda tidak dapat mengirimkan a
List<string>
.Juga, Anda perlu menyalin secara eksplisit semua konstruktor yang ingin Anda gunakan, dalam contoh StringList yang tidak dapat Anda katakan
new StringList(new[]{"a", "b", "c"})
, meskipunList<T>
mendefinisikan konstruktor seperti itu.Edit:
Jawaban yang diterima berfokus pada pro ketika menggunakan jenis turunan di dalam model domain Anda. Saya setuju bahwa mereka berpotensi berguna dalam kasus ini, masih, saya pribadi akan menghindarinya bahkan di sana.
Tetapi Anda harus (menurut saya) tidak pernah menggunakannya dalam API publik karena Anda membuat hidup pemanggil Anda lebih sulit atau menyia-nyiakan kinerja (saya juga tidak terlalu suka konversi tersirat pada umumnya).
sumber
Untuk apa nilainya, inilah contoh bagaimana mewarisi dari kelas generik digunakan dalam kompiler Roslyn Microsoft, dan bahkan tanpa mengubah nama kelas. (Saya sangat bingung dengan ini sehingga saya berakhir di sini dalam pencarian saya untuk melihat apakah ini benar-benar mungkin.)
Dalam ProjectAnalysis proyek Anda dapat menemukan definisi ini:
Kemudian dalam analisis proyek CSharpCode ada definisi ini:
Kelas PEModuleBuilder non-generik ini digunakan secara luas dalam proyek analisis CSharpCode, dan beberapa kelas dalam proyek itu mewarisi darinya, secara langsung atau tidak langsung.
Dan kemudian dalam proyek BasicCodeanalysis ada definisi ini:
Karena kita dapat (semoga) berasumsi bahwa Roslyn ditulis oleh orang-orang dengan pengetahuan luas tentang C # dan bagaimana seharusnya digunakan, saya berpikir bahwa ini adalah rekomendasi dari teknik ini.
sumber
Ada tiga kemungkinan alasan mengapa seseorang mencoba melakukan itu di luar kepala saya:
1) Untuk menyederhanakan deklarasi:
Tentu
StringList
danListString
kira-kira sama panjangnya tetapi bayangkan Anda sedang bekerja denganUserCollection
yang sebenarnyaDictionary<Tuple<string,Type>, IUserData<Dictionary,MySerializer>>
atau contoh generik besar lainnya.2) Untuk membantu AOT:
Terkadang Anda perlu menggunakan kompilasi Ahead Of Time, bukan Just In Time Compilation - mis. Pengembangan untuk iOS. Kadang-kadang kompiler AOT mengalami kesulitan mencari tahu semua jenis generik sebelumnya, dan ini bisa menjadi upaya untuk memberikannya petunjuk.
3) Untuk menambah fungsionalitas atau menghapus ketergantungan:
Mungkin mereka memiliki fungsi spesifik yang ingin mereka terapkan
StringList
(bisa disembunyikan dalam metode ekstensi, belum ditambahkan, atau historis) yang mereka tidak dapat tambahkanList<string>
tanpa mewarisi darinya, atau bahwa mereka tidak ingin mencemariList<string>
dengan .Atau mereka mungkin ingin mengubah implementasi dari
StringList
jalur, jadi baru saja menggunakan kelas sebagai penanda untuk saat ini (biasanya Anda akan membuat / menggunakan antarmuka untuk ini misalnyaIList
).Saya juga mencatat bahwa Anda telah menemukan kode ini di Irony, yang merupakan parser bahasa - jenis aplikasi yang tidak biasa. Mungkin ada beberapa alasan spesifik lainnya yang digunakan.
Contoh-contoh ini menunjukkan bahwa mungkin ada alasan yang sah untuk menulis pernyataan seperti itu - bahkan jika itu tidak terlalu umum. Seperti apa pun, pertimbangkan beberapa opsi dan pilih yang terbaik untuk Anda saat itu.
Jika Anda melihat kode sumbernya , tampaknya opsi 3 adalah alasannya - mereka menggunakan teknik ini untuk membantu menambah koleksi fungsionalitas / spesialisasi:
sumber
List<T>
itu, tetapi mereka harus memeriksaStringList
dalam konteks untuk benar-benar memahami apa itu . Pada kenyataannya, hanya sajaList<string>
, menggunakan nama yang berbeda. Tampaknya tidak ada keuntungan semantik untuk melakukan ini dalam kasus yang begitu sederhana. Anda benar-benar hanya mengganti tipe yang terkenal dengan tipe yang sama dengan nama yang berbeda.Menurut saya itu bukan praktik yang baik.
List<string>
mengkomunikasikan "daftar string umum". JelasList<string>
metode ekstensi berlaku. Diperlukan lebih sedikit kompleksitas untuk memahami; hanya Daftar yang dibutuhkan.StringList
mengkomunikasikan "kebutuhan akan kelas yang terpisah". MungkinList<string>
metode ekstensi berlaku (periksa implementasi tipe aktual untuk ini). Lebih banyak kompleksitas diperlukan untuk memahami kode; Daftar dan pengetahuan implementasi kelas turunan diperlukan.sumber
List<string>
.