Tidak seperti Java, mengapa C # memperlakukan metode sebagai fungsi non-virtual secara default? Apakah ini lebih cenderung menjadi masalah kinerja daripada hasil lain yang mungkin?
Saya teringat saat membaca paragraf dari Anders Hejlsberg tentang beberapa keuntungan yang dibawa oleh arsitektur yang ada. Tapi, bagaimana dengan efek sampingnya? Apakah benar-benar merupakan trade-off yang baik untuk memiliki metode non-virtual secara default?
c#
java
.net
virtual-functions
Burcu Dogan
sumber
sumber
this
referensinya null. Lihat di sini untuk info lebih lanjut.Jawaban:
Kelas harus dirancang untuk warisan agar dapat memanfaatkannya. Memiliki metode
virtual
secara default berarti bahwa setiap fungsi di kelas dapat dicolokkan dan diganti dengan yang lain, yang sebenarnya bukan hal yang baik. Banyak orang bahkan percaya bahwa kelas seharusnyasealed
secara default.virtual
metode juga dapat memiliki sedikit implikasi kinerja. Namun, ini sepertinya bukan alasan utama.sumber
callvirt
dengan yang sederhanacall
dalam kode IL (atau bahkan lebih jauh ke hilir saat JITting). Java HotSpot melakukan hal yang sama. Sisa jawabannya tepat.Saya terkejut bahwa tampaknya ada konsensus di sini bahwa non-virtual-by-default adalah cara yang tepat untuk melakukan sesuatu. Saya akan turun di sisi lain - saya pikir pragmatis - sisi pagar.
Sebagian besar pembenaran bagi saya seperti argumen lama "Jika kami memberi Anda kekuatan, Anda mungkin melukai diri sendiri". Dari programmer ?!
Bagi saya, pembuat kode yang tidak cukup tahu (atau memiliki cukup waktu) untuk mendesain perpustakaan mereka untuk warisan dan / atau ekstensibilitas adalah pembuat kode yang menghasilkan persis perpustakaan yang mungkin harus saya perbaiki atau ubah - persis perpustakaan tempat kemampuan untuk menimpa akan sangat berguna.
Berapa kali saya harus menulis kode penyelesaian yang jelek dan putus asa (atau meninggalkan penggunaan dan menggulung solusi alternatif saya sendiri) karena saya tidak dapat menimpa jauh, jauh melebihi berapa kali saya pernah digigit ( misalnya di Jawa) dengan menimpa di mana desainer mungkin tidak mempertimbangkan saya mungkin.
Non-virtual-by-default membuat hidup saya lebih sulit.
UPDATE: Telah ditunjukkan [dengan benar] bahwa saya sebenarnya tidak menjawab pertanyaan itu. Jadi - dan dengan permintaan maaf karena agak terlambat ....
Saya agak ingin dapat menulis sesuatu yang bernas seperti "C # mengimplementasikan metode sebagai non-virtual secara default karena keputusan yang buruk dibuat yang menghargai program lebih tinggi daripada programmer". (Saya pikir itu bisa dibenarkan berdasarkan beberapa jawaban lain untuk pertanyaan ini - seperti kinerja (pengoptimalan prematur, siapa saja?), Atau menjamin perilaku kelas.)
Namun, saya menyadari bahwa saya hanya akan menyatakan pendapat saya dan bukan jawaban pasti yang diinginkan Stack Overflow. Tentunya, saya pikir, pada tingkat tertinggi jawaban definitif (tetapi tidak membantu) adalah:
Mereka non-virtual secara default karena desainer bahasa harus membuat keputusan dan itulah yang mereka pilih.
Sekarang saya rasa alasan pasti mereka membuat keputusan itu, kami tidak akan pernah .... oh, tunggu! Transkrip percakapan!
Jadi tampaknya jawaban dan komentar di sini tentang bahaya override API dan kebutuhan untuk mendesain secara eksplisit untuk pewarisan berada di jalur yang benar tetapi semuanya kehilangan aspek temporal yang penting: Perhatian utama Anders adalah tentang mempertahankan kelas atau implisit API kontrak lintas versi . Dan saya pikir dia sebenarnya lebih peduli tentang mengizinkan platform .Net / C # untuk berubah di bawah kode daripada peduli tentang perubahan kode pengguna di atas platform. (Dan sudut pandang "pragmatis" nya adalah kebalikan dari saya karena dia melihat dari sisi lain.)
(Tapi tidak bisakah mereka memilih virtual-by-default dan kemudian membumbui "final" melalui basis kode? Mungkin itu tidak persis sama .. dan Anders jelas lebih pintar dari saya jadi saya akan membiarkannya berbohong.)
sumber
virtual
... Visual Studio add-in anyone?Karena terlalu mudah untuk melupakan bahwa suatu metode dapat diganti dan tidak dirancang untuk itu. C # membuat Anda berpikir sebelum menjadikannya virtual. Saya pikir ini adalah keputusan desain yang bagus. Beberapa orang (seperti Jon Skeet) bahkan mengatakan bahwa kelas harus ditutup secara default.
sumber
Untuk meringkas apa yang orang lain katakan, ada beberapa alasan:
1- Dalam C #, ada banyak hal dalam sintaksis dan semantik yang berasal langsung dari C ++. Fakta bahwa metode di mana tidak-virtual secara default di C ++ mempengaruhi C #.
2- Memiliki setiap metode virtual secara default adalah masalah kinerja karena setiap metode panggilan harus menggunakan Tabel Virtual objek. Selain itu, hal ini sangat membatasi kemampuan compiler Just-In-Time untuk menyebariskan metode dan melakukan jenis pengoptimalan lainnya.
3- Yang terpenting, jika metode tidak virtual secara default, Anda dapat menjamin perilaku kelas Anda. Ketika mereka virtual secara default, seperti di Java, Anda bahkan tidak dapat menjamin bahwa metode pengambil sederhana akan melakukan seperti yang diinginkan karena dapat ditimpa untuk melakukan apa pun di kelas turunan (tentu saja Anda dapat, dan harus, membuat metode dan / atau kelas akhir).
Orang mungkin bertanya-tanya, seperti yang disebutkan Zifre, mengapa bahasa C # tidak melangkah lebih jauh dan membuat kelas disegel secara default. Itu adalah bagian dari seluruh perdebatan tentang masalah implementasi pewarisan, yang merupakan topik yang sangat menarik.
sumber
C # dipengaruhi oleh C ++ (dan lainnya). C ++ tidak mengaktifkan pengiriman dinamis (fungsi virtual) secara default. Salah satu argumen (bagus?) Untuk ini adalah pertanyaan: "Seberapa sering Anda menerapkan kelas yang merupakan anggota dari kelas hiearchy?". Alasan lain untuk menghindari pengaktifan pengiriman dinamis secara default adalah jejak memori. Kelas tanpa penunjuk virtual (vpointer) yang menunjuk ke tabel virtual , tentu saja lebih kecil dari kelas yang sesuai dengan pengikatan akhir diaktifkan.
Masalah kinerja tidak mudah untuk dikatakan "ya" atau "tidak". Alasan untuk ini adalah kompilasi Just In Time (JIT) yang merupakan pengoptimalan waktu berjalan di C #.
Pertanyaan lain yang serupa tentang " kecepatan panggilan virtual .. "
sumber
Alasan sederhananya adalah biaya desain dan perawatan selain biaya kinerja. Metode virtual memiliki biaya tambahan dibandingkan dengan metode non-virtual karena desainer kelas harus merencanakan apa yang terjadi ketika metode tersebut diganti oleh kelas lain. Ini berdampak besar jika Anda mengharapkan metode tertentu untuk memperbarui status internal atau memiliki perilaku tertentu. Anda sekarang harus merencanakan apa yang terjadi ketika kelas turunan mengubah perilaku itu. Jauh lebih sulit untuk menulis kode yang dapat diandalkan dalam situasi itu.
Dengan metode non-virtual Anda memiliki kendali penuh. Apa pun yang salah adalah kesalahan penulis aslinya. Kode jauh lebih mudah untuk dipikirkan.
sumber
Jika semua metode C # adalah virtual maka vtbl akan jauh lebih besar.
Objek C # hanya memiliki metode virtual jika kelas memiliki metode virtual yang ditentukan. Memang benar bahwa semua objek memiliki informasi tipe yang menyertakan ekivalen vtbl, tetapi jika tidak ada metode virtual yang ditentukan maka hanya metode Objek dasar yang akan ditampilkan.
@ Tom Hawtin: Mungkin lebih akurat untuk mengatakan bahwa C ++, C # dan Java semuanya berasal dari rumpun bahasa C :)
sumber
Berasal dari latar belakang perl saya pikir C # menutup malapetaka dari setiap pengembang yang mungkin ingin memperluas dan memodifikasi perilaku kelas dasar 'melalui metode non virtual tanpa memaksa semua pengguna kelas baru untuk menyadari potensi di balik layar detailnya.
Pertimbangkan metode Tambahkan kelas Daftar. Bagaimana jika pengembang ingin memperbarui salah satu dari beberapa basis data potensial setiap kali Daftar tertentu 'Ditambahkan' ke? Jika 'Tambah' adalah virtual secara default, pengembang dapat mengembangkan kelas 'BackedList' yang menggantikan metode 'Tambah' tanpa memaksa semua kode klien untuk mengetahui bahwa itu adalah 'BackedList' dan bukan 'List' biasa. Untuk semua tujuan praktis 'BackedList' dapat dilihat hanya sebagai 'List' dari kode klien.
Ini masuk akal dari perspektif kelas utama yang besar yang mungkin menyediakan akses ke satu atau lebih komponen daftar yang didukung oleh satu atau beberapa skema dalam database. Mengingat bahwa metode C # tidak virtual secara default, daftar yang disediakan oleh kelas utama tidak dapat berupa IEnumerable atau ICollection sederhana atau bahkan contoh List tetapi harus diiklankan ke klien sebagai 'BackedList' untuk memastikan bahwa versi baru operasi 'Tambah' dipanggil untuk memperbarui skema yang benar.
sumber
B
adaA
. JikaB
membutuhkan sesuatu yang berbeda dariA
maka tidakA
. Saya percaya mengesampingkan sebagai kemampuan dalam bahasa itu sendiri adalah cacat desain. Jika Anda membutuhkanAdd
metode yang berbeda , maka kelas koleksi Anda bukanList
. Mencoba mengatakan itu palsu. Pendekatan yang tepat di sini adalah komposisi (dan bukan pemalsuan). Benar, seluruh kerangka dibangun di atas kemampuan utama, tapi saya tidak menyukainya.Ini tentu bukan masalah kinerja. Interpreter Java Sun menggunakan kode yang sama untuk mengirimkan (
invokevirtual
bytecode) dan HotSpot menghasilkan kode yang sama persisfinal
atau tidak. Saya percaya semua objek C # (tetapi bukan struct) memiliki metode virtual, jadi Anda akan selalu memerlukanvtbl
identifikasi kelas / runtime. C # adalah dialek dari "bahasa mirip Java". Untuk menyarankan itu berasal dari C ++ tidak sepenuhnya jujur.Ada gagasan bahwa Anda harus "merancang untuk warisan atau melarangnya". Kedengarannya seperti ide bagus hingga saat Anda memiliki kasus bisnis yang parah untuk diselesaikan dengan cepat. Mungkin mewarisi dari kode yang tidak Anda kontrol.
sumber
final
danfinal
metode efektif itu sepele. Perlu juga dicatat bahwa ia dapat melakukan bimorphic inlining, yaitu metode inline dengan dua implementasi berbeda.