Dalam bahasa berorientasi objek yang mendukung parameter tipe umum (juga dikenal sebagai templat kelas, dan polimorfisme parametrik, meskipun tentu saja setiap nama memiliki konotasi yang berbeda), sering kali mungkin untuk menentukan batasan tipe pada parameter tipe, sehingga diturunkan. dari tipe lain. Sebagai contoh, ini adalah sintaks dalam C #:
//for classes:
class ExampleClass<T> where T : I1 {
}
//for methods:
S ExampleMethod<S>(S value) where S : I2 {
...
}
Apa alasan untuk menggunakan tipe antarmuka aktual dari tipe yang dibatasi oleh antarmuka tersebut? Misalnya, apa alasan untuk membuat metode tanda tangan I2 ExampleMethod(I2 value)
?
object-oriented
type-systems
generics
GregRos
sumber
sumber
ref
parameter tipe nilai, mungkin sebenarnya memodifikasi tipe nilai.Jawaban:
Menggunakan versi parametrik memberi
Sebagai contoh acak, anggaplah kita memiliki metode yang menghitung akar persamaan kuadrat
Dan kemudian Anda ingin itu bekerja pada jenis lain seperti hal-hal selain
int
. Anda dapat menulis sesuatu sepertiMasalahnya adalah ini tidak mengatakan apa yang Anda inginkan. Ia mengatakan
Kita tidak bisa melakukan sesuatu seperti
int sol = solve(a, b, c)
jikaa
,b
, danc
adalahint
s karena kita tidak tahu bahwa metode ini akan mengembalikanint
pada akhirnya! Ini mengarah pada beberapa tarian canggung dengan downcasting dan berdoa jika kita ingin menggunakan solusi dalam ekspresi yang lebih besar.Di dalam fungsi, seseorang mungkin memberi kita pelampung, bigint, dan derajat dan kita harus menambahkan dan melipatgandakannya bersama-sama. Kami ingin secara statis menolak ini karena operasi antara 3 kelas ini akan menjadi omong kosong. Derajatnya mod 360 sehingga tidak akan terjadi
a.plus(b) = b.plus(a)
dan keriuhan serupa akan muncul.Jika kita menggunakan parametrik polimorfisme dengan subtyping, kita dapat mengesampingkan semua ini karena tipe kita sebenarnya mengatakan apa yang kita maksud
Atau dengan kata-kata "Jika Anda memberi saya beberapa jenis yang merupakan angka, saya dapat menyelesaikan persamaan dengan koefisien-koefisien itu".
Ini muncul di banyak tempat lain juga. Sumber lain baik contoh adalah fungsi yang abstrak lebih semacam wadah, ala
reverse
,sort
,map
, dllsumber
Num<int>
) sebagai argumen tambahan. Anda selalu dapat mengimplementasikan antarmuka untuk semua jenis melalui delegasi. Ini pada dasarnya adalah jenis kelas Haskell, kecuali jauh lebih membosankan untuk digunakan karena Anda harus secara eksplisit melewati antarmuka.Karena itulah yang kamu butuhkan ...
dua tanda tangan yang jelas berbeda. Yang pertama mengambil semua jenis yang mengimplementasikan antarmuka dan satu-satunya jaminan yang dibuat adalah bahwa nilai kembali memenuhi antarmuka.
Yang kedua mengambil semua jenis yang mengimplementasikan antarmuka dan menjamin bahwa itu akan mengembalikan setidaknya jenis itu lagi (daripada sesuatu yang memenuhi antarmuka yang kurang ketat).
Terkadang, Anda menginginkan jaminan yang lebih lemah. Terkadang Anda menginginkan yang lebih kuat.
sumber
Or
yang mengambil duaParser
objek (kelas dasar abstrak, tetapi prinsipnya berlaku) dan mengembalikan yang baruParser
(tetapi dengan jenis yang berbeda). Pengguna akhir tidak boleh tahu atau peduli apa jenis betonnya.IEnumerable<T>
, mengembalikan yang lain,IEnumerable<T>
misalnya, sebenarnyaOrderedEnumerable<T>
)Penggunaan generik terbatas untuk parameter metode dapat memungkinkan metode untuk kembali jenisnya berdasarkan hal yang dilewatkan. Dalam. NET mereka dapat memiliki keuntungan tambahan juga. Diantara mereka:
Suatu metode yang menerima generik terbatas sebagai parameter
ref
atauout
dapat melewati variabel yang memenuhi kendala; sebaliknya, metode non-generik dengan parameter tipe antarmuka akan terbatas pada menerima variabel dari tipe antarmuka yang tepat.Metode dengan tipe T parameter generik dapat menerima koleksi generik dari T. Metode yang menerima
IList<T> where T:IAnimal
akan dapat menerimaList<SiameseCat>
, tetapi metode yang ingin danIList<Animal>
tidak akan dapat melakukannya.Kendala terkadang dapat menentukan antarmuka dalam hal tipe generik, misalnya
where T:IComparable<T>
.Struktur yang mengimplementasikan antarmuka dapat disimpan sebagai tipe nilai ketika diteruskan ke metode yang menerima parameter generik terbatas, tetapi harus dikotakkan bila dilewatkan sebagai tipe antarmuka. Ini dapat memiliki efek besar pada kecepatan.
Parameter generik dapat memiliki beberapa kendala, sementara tidak ada cara lain untuk menentukan parameter "beberapa tipe yang mengimplementasikan IFoo dan IBar". Kadang-kadang ini bisa menjadi pedang bermata dua, karena kode yang telah menerima parameter tipe
IFoo
akan merasa sangat sulit untuk meneruskannya ke metode seperti itu yang mengharapkan generik berlipat ganda, bahkan jika instance yang dipermasalahkan akan memenuhi semua kendala.Jika dalam situasi tertentu tidak akan ada keuntungan menggunakan generik, maka cukup menerima parameter dari jenis antarmuka. Penggunaan generik akan memaksa sistem tipe dan JITter untuk melakukan pekerjaan ekstra, jadi jika tidak ada manfaatnya, orang tidak seharusnya melakukannya. Di sisi lain, sangat umum bahwa setidaknya satu dari keuntungan di atas akan berlaku.
sumber