=============
UPDATE: Saya menggunakan jawaban ini sebagai dasar untuk entri blog ini:
Mengapa parameter ref dan out tidak mengizinkan variasi jenis?
Lihat halaman blog untuk komentar lebih lanjut tentang masalah ini. Terima kasih atas pertanyaan bagusnya.
=============
Mari kita misalkan Anda memiliki kelas Animal
, Mammal
, Reptile
, Giraffe
, Turtle
dan Tiger
, dengan hubungan subclassing jelas.
Sekarang misalkan Anda memiliki metode void M(ref Mammal m)
. M
bisa membaca dan menulis m
.
Bisakah Anda mengirimkan variabel tipe Animal
ke M
?
Tidak. Variabel tersebut dapat berisi a Turtle
, tetapi M
akan menganggap bahwa variabel tersebut hanya berisi Mamalia. A Turtle
bukan Mammal
.
Kesimpulan 1 : ref
Parameter tidak bisa dibuat "lebih besar". (Ada lebih banyak hewan daripada mamalia, jadi variabelnya menjadi "lebih besar" karena bisa berisi lebih banyak hal.)
Bisakah Anda mengirimkan variabel tipe Giraffe
ke M
?
Tidak. M
Bisa menulis ke m
, dan M
mungkin ingin menulis Tiger
ke m
. Sekarang Anda telah memasukkan a Tiger
ke dalam variabel yang sebenarnya bertipe Giraffe
.
Kesimpulan 2 : ref
Parameter tidak bisa dibuat "lebih kecil".
Sekarang pertimbangkan N(out Mammal n)
.
Bisakah Anda mengirimkan variabel tipe Giraffe
ke N
?
Tidak. N
Dapat menulis ke n
, dan N
mungkin ingin menulis Tiger
.
Kesimpulan 3 : out
Parameter tidak bisa dibuat "lebih kecil".
Bisakah Anda mengirimkan variabel tipe Animal
ke N
?
Hmm.
Nah, kenapa tidak? N
tidak bisa membaca n
, hanya bisa menulis padanya, bukan? Anda menulis Tiger
ke variabel tipe Animal
dan Anda sudah siap, bukan?
Salah. Aturannya bukanlah " N
hanya dapat menulis ke n
".
Aturannya adalah, secara singkat:
1) N
harus menulis n
sebelum N
kembali normal. (Jika N
melempar, semua taruhan dibatalkan.)
2) N
harus menulis sesuatu n
sebelum membaca sesuatu dari n
.
Itu memungkinkan urutan kejadian ini:
- Deklarasikan bidang
x
tipe Animal
.
- Teruskan
x
sebagai out
parameter ke N
.
N
menulis Tiger
ke n
, yang merupakan alias untuk x
.
- Di utas lain, seseorang menulis
Turtle
ke x
.
N
mencoba membaca isi dari n
, dan menemukan a Turtle
dalam apa yang dianggapnya sebagai variabel tipe Mammal
.
Jelas kami ingin membuatnya ilegal.
Kesimpulan 4 : out
Parameter tidak bisa dibuat "lebih besar".
Kesimpulan akhir : Baik parameter ref
maupun out
parameter tidak dapat mengubah jenisnya. Melakukan sebaliknya adalah merusak jenis keamanan yang dapat diverifikasi.
Jika masalah-masalah dalam teori tipe dasar ini menarik minat Anda, pertimbangkan untuk membaca seri saya tentang bagaimana kovarians dan kontravarian bekerja di C # 4.0 .
out
parameter tidak bisa dibuat "lebih besar"? Urutan yang Anda jelaskan dapat diterapkan ke variabel apa pun, tidak hanyaout
variabel parameter. Dan juga pembaca perlu memberikan nilai argumenMammal
sebelum dia mencoba mengaksesnya karenaMammal
dan tentu saja bisa gagal jika dia tidak perhatianKarena dalam kedua kasus Anda harus dapat menetapkan nilai ke parameter ref / out.
Jika Anda mencoba memasukkan b ke dalam metode Foo2 sebagai referensi, dan di Foo2 Anda mencoba memasukkan a = A baru (), ini tidak valid.
Alasan yang sama Anda tidak bisa menulis:
sumber
Anda sedang berjuang dengan masalah OOP klasik dari kovarian (dan kontravarian), lihat wikipedia : karena fakta ini mungkin menentang ekspektasi intuitif, secara matematis tidak mungkin untuk mengizinkan substitusi kelas turunan sebagai pengganti dari kelas dasar untuk argumen yang dapat diubah (dapat dialihkan) (dan juga kontainer yang barangnya dapat dialihkan, dengan alasan yang sama) dengan tetap menghormati prinsip Liskov . Mengapa demikian dijelaskan dalam jawaban yang ada, dan dieksplorasi lebih dalam di artikel wiki ini dan tautannya.
Bahasa OOP yang tampaknya melakukannya sementara tetap tradisional secara statis aman jenis "curang" (memasukkan pemeriksaan jenis dinamis tersembunyi, atau memerlukan pemeriksaan waktu kompilasi dari SEMUA sumber untuk diperiksa); pilihan dasarnya adalah: menyerah pada kovarian ini dan menerima kebingungan praktisi (seperti yang dilakukan C # di sini), atau beralih ke pendekatan pengetikan dinamis (seperti bahasa OOP pertama, Smalltalk, lakukan), atau pindah ke tidak dapat diubah (tunggal- assignment) data, seperti bahasa fungsional (di bawah immutability, Anda dapat mendukung kovarians, dan juga menghindari teka-teki terkait lainnya seperti fakta bahwa Anda tidak dapat memiliki persegi subclass Rectangle di dunia data yang bisa berubah).
sumber
Mempertimbangkan:
Itu akan melanggar keamanan tipe
sumber
var
itu benar-benar salah. Tetap.Sementara tanggapan lain secara ringkas menjelaskan alasan di balik perilaku ini, saya pikir perlu disebutkan bahwa jika Anda benar-benar perlu melakukan sesuatu seperti ini, Anda dapat mencapai fungsi serupa dengan membuat Foo2 menjadi metode umum, seperti:
sumber
Karena memberikan
Foo2
sebuahref B
akan menghasilkan sebuah objek cacat karenaFoo2
hanya tahu bagaimana untuk mengisiA
bagian dariB
.sumber
Bukankah itu kompiler yang memberi tahu Anda bahwa ia ingin Anda mentransmisikan objek secara eksplisit sehingga dapat yakin Anda tahu apa maksud Anda?
sumber
Masuk akal dari perspektif keamanan, tetapi saya lebih suka jika kompilator memberikan peringatan daripada kesalahan, karena ada penggunaan sah dari objek polimoprik yang diteruskan oleh referensi. misalnya
Ini tidak akan bisa dikompilasi, tapi akankah berhasil?
sumber
Jika Anda menggunakan contoh praktis untuk tipe Anda, Anda akan melihatnya:
Dan sekarang Anda memiliki fungsi yang mengambil leluhur ( yaitu
Object
):Apa yang salah dengan itu?
Anda baru saja berhasil menetapkan
Bitmap
ke AndaSqlConnection
.Itu tidak baik.
Coba lagi dengan orang lain:
Anda memasukkan
OracleConnection
over-top AndaSqlConnection
.sumber
Dalam kasus saya, fungsi saya menerima suatu objek dan saya tidak dapat mengirim apa pun jadi saya melakukannya
Dan itu berhasil
Foo saya ada di VB.NET dan memeriksa tipe di dalamnya dan melakukan banyak logika
Saya minta maaf jika jawaban saya duplikat tetapi yang lain terlalu panjang
sumber