Downcasting berarti casting dari kelas dasar (atau antarmuka) ke kelas subclass atau daun.
Contoh downcast mungkin jika Anda beralih dari System.Object
ke jenis lainnya.
Downcasting tidak populer, mungkin bau kode: Doktrin Berorientasi Objek adalah lebih suka, misalnya, mendefinisikan dan memanggil metode virtual atau abstrak daripada downcasting.
- Apa, jika ada, kasus penggunaan yang baik dan tepat untuk downcasting? Artinya, dalam keadaan apa sajakah yang tepat untuk menulis kode yang mengecewakan?
- Jika jawaban Anda adalah "tidak ada", lalu mengapa downcasting didukung oleh bahasa?
c#
object-oriented
ChrisW
sumber
sumber
ToString
); contoh lainnya adalah "wadah Java" karena Java tidak mendukung obat generik (yang C # tidak). stackoverflow.com/questions/1524197/downcast-and-upcast mengatakan apa downcasting adalah , tetapi tanpa contoh ketika itu tepat .before Java had support for generics
, tidakbecause Java doesn't support generics
. Dan Amon cukup banyak memberi Anda jawaban - ketika sistem tipe yang Anda gunakan terlalu membatasi dan Anda tidak dalam posisi untuk mengubahnya.Jawaban:
Berikut adalah beberapa penggunaan downcasting yang tepat.
Dan saya dengan penuh hormat sangat tidak setuju dengan orang lain di sini yang mengatakan penggunaan downcast jelas merupakan bau kode, karena saya percaya tidak ada cara lain yang masuk akal untuk menyelesaikan masalah yang terkait.
Equals
:Clone
:Stream.EndRead/Write
:Jika Anda akan mengatakan ini bau kode, Anda perlu memberikan solusi yang lebih baik untuk mereka (bahkan jika mereka dihindari karena ketidaknyamanan). Saya tidak percaya ada solusi yang lebih baik.
sumber
object.Equals
cocok dengan deskripsi itu dengan sangat baik (cobalah untuk memberikan kontrak yang setara dengan benar dalam superclass yang memungkinkan subkelas untuk mengimplementasikannya dengan benar - itu benar-benar non-sepele). Bahwa sebagai pemrogram aplikasi kita tidak dapat menyelesaikannya (dalam beberapa kasus kita dapat menghindarinya) adalah topik yang berbeda.Object.Equals is horrid. Equality computation in C# is completely messed up
(komentar Eric pada jawabannya). Sangat lucu bagaimana Anda tidak ingin mempertimbangkan kembali pendapat Anda, bahkan ketika salah satu perancang utama bahasa itu sendiri memberi tahu Anda bahwa Anda salah.Saya tidak setuju. Downcasting sangat populer ; sejumlah besar program di dunia nyata berisi satu atau lebih downcast. Dan itu mungkin bukan bau kode. Ini jelas merupakan bau kode. Itu sebabnya operasi downcasting diperlukan untuk menjadi nyata dalam teks program . Ini agar Anda bisa lebih mudah memperhatikan aroma dan menghabiskan perhatian review kode di atasnya.
Dalam keadaan apa pun di mana:
Jika Anda bisa dengan murah merevisi sebuah program sehingga tipe runtime dapat dideduksi oleh kompiler, atau untuk merevisi program sehingga Anda tidak memerlukan kemampuan dari tipe yang lebih diturunkan, maka lakukanlah. Orang-orang yang tertekan ditambahkan ke bahasa untuk keadaan-keadaan di mana sulit dan mahal untuk dengan demikian memperbaiki program.
C # ditemukan oleh programmer pragmatis yang memiliki pekerjaan untuk dilakukan, untuk programmer pragmatis yang memiliki pekerjaan untuk dilakukan. Desainer C # bukanlah puritan OO. Dan sistem tipe C # tidak sempurna; oleh desain itu meremehkan pembatasan yang dapat ditempatkan pada jenis runtime dari suatu variabel.
Juga, downcasting sangat aman di C #. Kami memiliki jaminan kuat bahwa downcast akan diverifikasi pada saat runtime dan jika tidak dapat diverifikasi maka program melakukan hal yang benar dan crash. Ini luar biasa; itu berarti bahwa jika 100% pemahaman Anda yang benar tentang tipe semantik ternyata 99,99% benar, maka program Anda bekerja 99,99% dari waktu dan crash sisa waktu, bukannya berperilaku tak terduga dan merusak data pengguna 0,01% dari waktu .
LATIHAN: Setidaknya ada satu cara untuk menghasilkan downcast dalam C # tanpa menggunakan operator pemeran eksplisit. Bisakah Anda memikirkan skenario seperti itu? Karena ini juga merupakan bau kode yang potensial, menurut Anda faktor desain apa yang masuk ke dalam desain fitur yang dapat menghasilkan crash yang terputus-putus tanpa ada pemain nyata dalam kode tersebut?
sumber
Object.Equals
adalah mengerikan . Perhitungan kesetaraan dalam C # benar - benar kacau , terlalu kacau untuk dijelaskan dalam komentar. Ada begitu banyak cara untuk mewakili kesetaraan; sangat mudah untuk membuatnya tidak konsisten; itu sangat mudah, pada kenyataannya diharuskan untuk melanggar sifat-sifat yang disyaratkan dari operator kesetaraan (simetri, refleksivitas dan transitivitas). Apakah saya merancang sistem tipe dari awal hari ini tidak akan adaEquals
(atauGetHashcode
atauToString
)Object
sama sekali.Penangan acara biasanya memiliki tanda tangan
MethodName(object sender, EventArgs e)
. Dalam beberapa kasus mungkin untuk menangani acara tanpa memperhatikan apa jenisnyasender
, atau bahkan tanpa menggunakansender
sama sekali, tetapi dalam kasus lainsender
harus dilemparkan ke jenis yang lebih spesifik untuk menangani acara tersebut.Misalnya, Anda mungkin memiliki dua
TextBox
dan Anda ingin menggunakan satu delegasi untuk menangani suatu acara dari masing-masing. Kemudian Anda akan perlu untuk membuangsender
keTextBox
sehingga Anda bisa mengakses sifat yang diperlukan untuk menangani acara:Ya, dalam contoh ini, Anda dapat membuat subkelas Anda sendiri
TextBox
yang melakukan ini secara internal. Namun, tidak selalu praktis atau mungkin.sumber
TextChanged
event adalah anggotaControl
- saya bertanya-tanya mengapasender
tidak dari jenisControl
bukan jenisobject
? Saya juga bertanya-tanya apakah (secara teoritis) kerangka kerja dapat mendefinisikan ulang acara untuk setiap subkelas (sehingga misalnyaTextBox
dapat memiliki versi baru dari acara tersebut, menggunakanTextBox
sebagai jenis pengirim) ... yang mungkin (atau mungkin tidak) memerlukan downcasting ( toTextBox
) di dalamTextBox
implementasi (untuk mengimplementasikan tipe acara baru), tetapi akan menghindari keharusan downcast dalam event handler kode aplikasi.TextBox sender
daripadaobject sender
tanpa terlalu rumit kode, saya yakin hal itu akan dilakukan.Anda perlu downcasting ketika sesuatu memberi Anda supertype dan Anda perlu menanganinya secara berbeda tergantung pada subtipe.
Tidak, ini baunya tidak enak. Dalam dunia yang sempurna
Donation
akan memiliki metode abstrakgetValue
dan setiap subtipe implementasi akan memiliki implementasi yang tepat.Tetapi bagaimana jika kelas-kelas ini dari perpustakaan Anda tidak bisa atau tidak ingin berubah? Maka tidak ada pilihan lain.
sumber
dynamic
kata kunci yang mencapai hasil yang sama.void accept(IDonationVisitor visitor)
, yang diimplementasikan oleh subkelasnya dengan memanggil metode spesifik (misalnya kelebihan beban)IDonationVisitor
.dynamic
kata kunci.dynamic
masih downcasting, hanya lebih lambat.Untuk menambah jawaban Eric Lippert karena saya tidak bisa berkomentar ...
Anda sering dapat menghindari downcasting dengan refactoring API untuk menggunakan obat generik. Tetapi obat generik tidak ditambahkan ke bahasa sampai versi 2.0 . Jadi, bahkan jika kode Anda sendiri tidak perlu mendukung versi bahasa kuno, Anda mungkin menemukan diri Anda menggunakan kelas-kelas lama yang tidak parameter. Sebagai contoh, beberapa kelas mungkin didefinisikan untuk membawa payload tipe
Object
, yang Anda dipaksa untuk melemparkan ke tipe yang Anda tahu sebenarnya.sumber
Ada pertukaran antara bahasa statis dan diketik secara dinamis. Pengetikan statis memberi kompiler banyak informasi untuk dapat membuat jaminan yang agak kuat tentang keamanan (bagian dari) program. Namun, hal ini berbiaya, karena Anda tidak hanya perlu tahu bahwa program Anda benar, tetapi Anda harus menulis kode yang sesuai untuk meyakinkan kompiler bahwa ini adalah masalahnya. Dengan kata lain, membuat klaim lebih mudah daripada membuktikannya.
Ada konstruksi "tidak aman" yang membantu Anda membuat pernyataan yang tidak terbukti ke kompiler. Misalnya, panggilan tanpa syarat ke
Nullable.Value
, downcast tanpa syarat,dynamic
objek, dll. Mereka memungkinkan Anda untuk mengajukan klaim ("Saya menyatakan bahwa objeka
adalahString
, jika saya salah, melemparInvalidCastException
") tanpa perlu membuktikannya. Ini dapat berguna dalam kasus-kasus di mana membuktikannya secara signifikan lebih sulit daripada bermanfaat.Menyalahgunakan ini berisiko, itulah sebabnya mengapa notasi downcast eksplisit ada dan wajib; itu garam sintaksis dimaksudkan untuk menarik perhatian operasi yang tidak aman. Bahasa tersebut dapat diimplementasikan dengan downcasting implisit (di mana jenis yang disimpulkan tidak ambigu), tetapi itu akan menyembunyikan operasi yang tidak aman ini, yang tidak diinginkan.
sumber
Downcasting dianggap buruk karena beberapa alasan. Terutama saya pikir karena itu anti-OOP.
OOP akan sangat menyukainya jika Anda tidak pernah harus tertunduk karena '' raison-d'etre '' adalah bahwa polimorfisme berarti Anda tidak perlu pergi
kamu lakukan saja
dan kode secara otomatis melakukan hal yang benar.
Ada beberapa alasan 'sulit' untuk tidak tertunduk:
Tetapi alternatif untuk downcasting dalam beberapa skenario bisa sangat jelek.
Contoh klasiknya adalah pemrosesan pesan, di mana Anda tidak ingin menambahkan fungsi pemrosesan ke objek, tetapi simpan di pengolah pesan. Saya kemudian memiliki satu ton
MessageBaseClass
dalam array untuk diproses, tetapi saya juga membutuhkan masing-masing dengan subtipe untuk pemrosesan yang benar.Anda dapat menggunakan Double Dispatch untuk menyelesaikan masalah ( https://en.wikipedia.org/wiki/Double_dispatch ) ... Tetapi itu juga memiliki masalah. Atau Anda bisa downcast untuk beberapa kode sederhana dengan risiko alasan-alasan sulit itu.
Tapi ini sebelum Generik ditemukan. Sekarang Anda dapat menghindari downcasting dengan menyediakan jenis yang detailnya ditentukan kemudian.
MessageProccesor<T>
dapat memvariasikan parameter metode dan mengembalikan nilai menurut tipe yang ditentukan tetapi tetap menyediakan fungsionalitas umumTentu saja tidak ada yang memaksa Anda untuk menulis kode OOP dan ada banyak fitur bahasa yang disediakan tetapi disukai, seperti refleksi.
sumber
static_cast
bukandynamic_cast
.lParam
ke sesuatu yang spesifik. Tapi saya tidak yakin itu contoh C # yang bagus.static_cast
selalu cepat ... tetapi tidak dijamin tidak akan gagal dengan cara yang tidak ditentukan jika Anda melakukan kesalahan dan tipenya tidak seperti yang Anda harapkan.Selain semua yang dikatakan sebelumnya, bayangkan properti Tag yang bertipe objek. Ini memberikan cara bagi Anda untuk menyimpan objek pilihan Anda di objek lain untuk digunakan nanti yang Anda butuhkan. Anda perlu downcast properti ini.
Secara umum, tidak menggunakan sesuatu sampai hari ini tidak selalu merupakan indikasi sesuatu yang tidak berguna ;-)
sumber
Control
(alih-alih menggunakanobject Tag
properti) adalah dengan membuatDictionary<Control, string>
label untuk setiap kontrol.Tag
merupakan properti publik dari kelas .Net framework, yang menunjukkan bahwa Anda (kurang lebih) seharusnya menggunakannya.System.Collections.ArrayList
) atau usang (katakanlah,IEnumerator.Reset
).Penggunaan downcasting yang paling umum dalam pekerjaan saya adalah karena beberapa orang idiot yang melanggar prinsip Pergantian Liskov.
Bayangkan ada antarmuka di beberapa lib pihak ketiga.
Dan 2 kelas yang mengimplementasikannya.
Bar
danQux
.Bar
berperilaku baik.Tetapi
Qux
tidak berperilaku baik.Baiklah ... sekarang saya tidak punya pilihan. Saya harus mengetik cek dan (mungkin) downcast untuk menghindari program saya terhenti.
sumber
var qux = foo as Qux;
.Contoh dunia nyata lainnya adalah WPF
VisualTreeHelper
. Ini menggunakan downcast untuk melemparkanDependencyObject
keVisual
danVisual3D
. Misalnya, VisualTreeHelper.GetParent . Downcast terjadi di VisualTreeUtils.AsVisualHelper :sumber