Kata kunci teman C ++ memungkinkan class A
untuk menunjuk class B
sebagai temannya. Ini memungkinkan Class B
untuk mengakses private
/ protected
anggota class A
.
Saya tidak pernah membaca apa pun mengapa ini ditinggalkan dari C # (dan VB.NET). Sebagian besar jawaban untuk pertanyaan StackOverflow sebelumnya tampaknya mengatakan itu adalah bagian yang berguna dari C ++ dan ada alasan bagus untuk menggunakannya. Dalam pengalaman saya, saya harus setuju.
Pertanyaan lain bagi saya tampaknya benar-benar bertanya bagaimana melakukan sesuatu yang mirip dengan friend
aplikasi C #. Walaupun jawaban umumnya berkisar pada kelas bersarang, sepertinya tidak seelegan menggunakan friend
kata kunci.
Buku Pola Desain asli menggunakannya secara teratur di seluruh contohnya.
Jadi secara ringkas, mengapa tidak friend
ada dari C #, dan apa cara "praktik terbaik" (atau cara) mensimulasikannya dalam C #?
(Omong-omong, internal
kata kunci bukan hal yang sama, itu memungkinkan semua kelas dalam seluruh majelis untuk mengakses internal
anggota, sementara friend
memungkinkan Anda untuk memberikan kelas tertentu akses lengkap ke tepat satu kelas lain)
Jawaban:
Memiliki teman dalam pemrograman lebih atau kurang dianggap "kotor" dan mudah disalahgunakan. Ini memutus hubungan antara kelas dan merusak beberapa atribut mendasar dari bahasa OO.
Yang sedang berkata, ini adalah fitur yang bagus dan saya sudah sering menggunakannya di C ++; dan ingin menggunakannya dalam C # juga. Tapi saya bertaruh karena OOness "murni" C # (dibandingkan dengan pseudo OOness C ++) MS memutuskan bahwa karena Java tidak memiliki kata kunci teman C # tidak seharusnya (hanya bercanda;))
Pada catatan yang serius: internal tidak sebaik teman tetapi itu menyelesaikan pekerjaan. Ingatlah bahwa Anda jarang mendistribusikan kode Anda ke pengembang pihak ketiga tidak melalui DLL; jadi selama Anda dan tim Anda tahu tentang kelas internal dan penggunaannya Anda harus baik-baik saja.
EDIT Biarkan saya menjelaskan bagaimana kata kunci teman melemahkan OOP.
Variabel dan metode pribadi dan yang dilindungi mungkin merupakan salah satu bagian terpenting dari OOP. Gagasan bahwa objek dapat menyimpan data atau logika yang hanya dapat mereka gunakan memungkinkan Anda untuk menulis implementasi fungsionalitas Anda terlepas dari lingkungan Anda - dan bahwa lingkungan Anda tidak dapat mengubah informasi keadaan yang tidak cocok untuk ditangani. Dengan menggunakan teman Anda menggabungkan implementasi dua kelas bersama-sama - yang jauh lebih buruk jika Anda hanya menambahkan antarmuka mereka.
sumber
friend
tidak membiarkan kelas atau fungsi sewenang-wenang mengakses anggota pribadi. Ini memungkinkan fungsi atau kelas tertentu yang ditandai sebagai teman. Kelas yang memiliki anggota pribadi masih mengontrol akses ke sana 100%. Anda mungkin juga berpendapat bahwa metode anggota publik. Keduanya memastikan bahwa metode yang secara khusus tercantum dalam kelas diberikan akses ke anggota kelas pribadi.Di samping catatan. Menggunakan teman bukan tentang melanggar enkapsulasi, tetapi sebaliknya tentang menegakkannya. Seperti accessor + mutators, operator overloading, warisan publik, downcasting, dll , sering disalahgunakan, tetapi itu tidak berarti kata kunci tidak memiliki, atau lebih buruk, tujuan yang buruk.
Lihat Konrad Rudolph 's pesan di thread lain, atau jika Anda lebih suka melihat entri yang relevan dalam C ++ FAQ.
sumber
friend
memberikannya akses ke SEMUA anggota pribadi Anda, bahkan jika Anda hanya perlu membiarkannya mengatur salah satunya. Ada argumen kuat yang dibuat bahwa membuat bidang tersedia untuk kelas lain yang tidak memerlukannya dianggap sebagai "melanggar enkapsulasi." Ada tempat di C ++ di mana diperlukan (operator kelebihan beban), tetapi ada tradeoff enkapsulasi yang perlu dipertimbangkan dengan hati-hati. Tampaknya para desainer C # merasa bahwa tradeoff tidak sepadan.Untuk info, lain terkait-tapi-tidak-cukup-the-sama hal di NET adalah
[InternalsVisibleTo]
, yang memungkinkan perakitan menunjuk lain perakitan (seperti tes unit perakitan) yang (efektif) memiliki "internal" akses ke jenis / anggota di perakitan asli.sumber
Anda harus dapat menyelesaikan hal-hal yang sama seperti yang digunakan "teman" di C ++ dengan menggunakan antarmuka di C #. Ini mengharuskan Anda untuk secara eksplisit menentukan anggota mana yang disahkan antara dua kelas, yang merupakan pekerjaan ekstra tetapi juga dapat membuat kode lebih mudah dimengerti.
Jika seseorang memiliki contoh penggunaan "teman" yang masuk akal yang tidak dapat disimulasikan menggunakan antarmuka, silakan bagikan! Saya ingin lebih memahami perbedaan antara C ++ dan C #.
sumber
Dengan
friend
C ++ desainer memiliki kontrol yang tepat atas siapa anggota * pribadi terkena. Tapi, dia terpaksa memaparkan setiap anggota pribadi.Dengan
internal
C # desainer memiliki kontrol yang tepat atas set anggota pribadi yang dia ungkap. Jelas, dia dapat mengekspos hanya satu anggota pribadi. Tapi, itu akan terkena semua kelas di majelis.Biasanya, seorang perancang ingin memaparkan hanya beberapa metode pribadi ke beberapa kelas lain yang dipilih. Sebagai contoh, dalam pola pabrik kelas mungkin diinginkan bahwa kelas C1 hanya dipakai oleh pabrik kelas CF1. Oleh karena itu kelas C1 dapat memiliki konstruktor yang dilindungi dan CF1 pabrik kelas teman.
Seperti yang Anda lihat, kami memiliki 2 dimensi di mana enkapsulasi dapat dilanggar.
friend
melanggar itu sepanjang satu dimensi,internal
apakah itu sepanjang yang lain. Mana yang merupakan pelanggaran yang lebih buruk dalam konsep enkapsulasi? Sulit untuk dikatakan. Tetapi akan menyenangkan untuk memiliki keduanyafriend
daninternal
tersedia. Selain itu, tambahan yang bagus untuk keduanya adalah jenis kata kunci ke-3, yang akan digunakan berdasarkan anggota-oleh-anggota (sukainternal
) dan menentukan kelas target (sepertifriend
).* Untuk singkatnya saya akan menggunakan "pribadi" bukan "pribadi dan / atau dilindungi".
- Nick
sumber
Bahkan, C # memberikan kemungkinan untuk mendapatkan perilaku yang sama dengan cara OOP murni tanpa kata-kata khusus - itu adalah antarmuka pribadi.
Sejauh pertanyaan. Apa yang dimaksud dengan C # yang setara dengan teman? ditandai sebagai duplikat untuk artikel ini dan tidak ada seorang pun di sana yang mengusulkan realisasi yang sangat bagus - saya akan menunjukkan jawaban pada kedua pertanyaan di sini.
Gagasan utama diambil dari sini: Apa itu antarmuka pribadi?
Katakanlah, kita memerlukan beberapa kelas yang dapat mengatur instance dari kelas lain dan memanggil beberapa metode khusus pada mereka. Kami tidak ingin memberikan kemungkinan untuk memanggil metode ini ke kelas lain. Ini persis sama dengan apa yang dilakukan teman c ++ kata kunci di dunia c ++.
Saya pikir contoh yang baik dalam praktek nyata bisa menjadi pola Full State Machine di mana beberapa kontroler memperbarui objek keadaan saat ini dan beralih ke objek keadaan lain bila perlu.
Anda bisa:
Controller.cs
Program.cs
Nah, bagaimana dengan warisan?
Kita perlu menggunakan teknik yang dijelaskan dalam Karena implementasi anggota antarmuka yang eksplisit tidak dapat dideklarasikan secara virtual dan tandai IState sebagai yang dilindungi untuk memberikan kemungkinan untuk diturunkan dari Pengontrol juga.
Controller.cs
PlayerIdleState.cs
Dan akhirnya contoh cara menguji class throw throw inheritance: ControllerTest.cs
Semoga saya mencakup semua kasus dan jawaban saya bermanfaat.
sumber
Teman sangat berguna saat menulis unit test.
Sementara itu datang dengan biaya mencemari deklarasi kelas Anda sedikit, itu juga merupakan pengingat yang dikompilasi oleh kompiler tentang tes apa yang mungkin benar-benar peduli dengan keadaan internal kelas.
Idiom yang sangat berguna dan bersih yang saya temukan adalah ketika saya memiliki kelas pabrik, menjadikan mereka teman dari barang yang mereka buat yang memiliki konstruktor yang dilindungi. Lebih khusus, ini adalah ketika saya memiliki satu pabrik yang bertanggung jawab untuk membuat objek rendering pencocokan untuk objek report report, rendering ke lingkungan tertentu. Dalam hal ini Anda memiliki satu titik pengetahuan tentang hubungan antara kelas penulis laporan (hal-hal seperti blok gambar, band tata letak, header halaman dll) dan objek rendering yang cocok.
sumber
C # tidak memiliki kata kunci "teman" karena alasan yang sama dengan kehancuran deterministik yang hilang. Mengubah konvensi membuat orang merasa pintar, seolah-olah cara baru mereka lebih unggul daripada cara lama orang lain. Ini semua tentang kebanggaan.
Mengatakan bahwa "kelas teman buruk" sama pendeknya dengan pernyataan tidak memenuhi syarat lainnya seperti "jangan gunakan gotos" atau "Linux lebih baik daripada Windows".
Kata kunci "teman" yang dikombinasikan dengan kelas proxy adalah cara yang bagus untuk hanya mengekspos bagian tertentu dari kelas ke kelas tertentu lainnya. Kelas proxy dapat bertindak sebagai penghalang tepercaya terhadap semua kelas lainnya. "publik" tidak mengizinkan penargetan seperti itu, dan menggunakan "dilindungi" untuk mendapatkan efek dengan warisan adalah canggung jika benar-benar tidak ada konseptual "adalah" hubungan.
sumber
Anda bisa mendekati C ++ "teman" dengan kata kunci C # "internal" .
sumber
internal
akan lebih masuk akal dan memberikan pertukaran yang sangat baik antara enkapsulasi dan utilitas. Akses standar Java bahkan lebih baik.Ini sebenarnya bukan masalah dengan C #. Ini adalah batasan mendasar dalam IL. C # dibatasi oleh ini, seperti bahasa .Net lainnya yang berupaya diverifikasi. Batasan ini juga termasuk kelas terkelola yang didefinisikan dalam C ++ / CLI ( Spec bagian 20.5 ).
Yang sedang berkata saya pikir Nelson memiliki penjelasan yang baik tentang mengapa ini adalah hal yang buruk.
sumber
friend
bukan hal yang buruk; sebaliknya, itu jauh lebih baik daripadainternal
. Dan penjelasan Nelson buruk dan salah.Berhentilah membuat alasan untuk pembatasan ini. teman buruk, tetapi internal baik? mereka adalah hal yang sama, hanya teman yang memberi Anda kontrol yang lebih tepat atas siapa yang diizinkan untuk mengakses dan siapa yang tidak.
Ini untuk menegakkan paradigma enkapsulasi? jadi Anda harus menulis metode accessor dan sekarang apa? bagaimana Anda seharusnya menghentikan semua orang (kecuali metode kelas B) dari memanggil metode ini? Anda tidak dapat, karena Anda tidak dapat mengontrol ini juga, karena kehilangan "teman".
Tidak ada bahasa pemrograman yang sempurna. C # adalah salah satu bahasa terbaik yang pernah saya lihat, tetapi membuat alasan konyol untuk fitur yang hilang tidak membantu siapa pun. Dalam C ++, saya melewatkan sistem event / delegate yang mudah, refleksi (+ otomatis de / serialisasi) dan foreach, tetapi dalam C # Saya kehilangan operator yang berlebihan (ya, terus mengatakan kepada saya bahwa Anda tidak memerlukannya), parameter default, konstanta yang tidak dapat dielakkan, pewarisan berganda (yeah, terus katakan padaku bahwa Anda tidak membutuhkannya dan antarmuka merupakan pengganti yang memadai) dan kemampuan untuk memutuskan untuk menghapus instance dari memori (tidak, ini tidak terlalu buruk kecuali Anda adalah seorang Tinkerer)
sumber
||
/&&
membuat hubungan arus pendek tetap. Parameter default ditambahkan di C # 4.Ada InternalsVisibleToAttribute sejak. Net 3 tapi saya menduga mereka hanya menambahkannya untuk melayani rakitan pengujian setelah munculnya unit testing. Saya tidak dapat melihat banyak alasan lain untuk menggunakannya.
Ini bekerja di tingkat perakitan tetapi ia melakukan pekerjaan di mana internal tidak; yaitu, di mana Anda ingin mendistribusikan sebuah majelis tetapi menginginkan majelis yang tidak terdistribusi memiliki akses istimewa untuk itu.
Cukup benar mereka membutuhkan teman perakitan untuk memiliki kunci yang kuat untuk menghindari seseorang membuat teman pura-pura bersama dengan teman-teman Anda yang dilindungi.
sumber
Saya telah membaca banyak komentar cerdas tentang kata kunci "teman" & saya setuju apa itu hal yang berguna, tetapi saya pikir kata kunci "internal" kurang berguna, & mereka berdua masih buruk untuk pemrograman OO murni.
Apa yang kita miliki? (Mengatakan tentang "teman" Saya juga mengatakan tentang "internal")
Iya;
tidak menggunakan "teman" membuat kode lebih baik?
Menggunakan teman membuat beberapa masalah lokal, tidak menggunakannya membuat masalah bagi pengguna perpustakaan-kode.
solusi umum yang baik untuk bahasa pemrograman yang saya lihat seperti ini:
Apa yang Anda pikirkan? Saya pikir itu solusi berorientasi objek yang paling umum & murni. Anda dapat membuka akses metode apa pun yang Anda pilih ke kelas yang Anda inginkan.
sumber
Saya hanya akan menjawab pertanyaan "Bagaimana".
Ada begitu banyak jawaban di sini, namun saya ingin mengusulkan semacam "pola desain" untuk mencapai fitur itu. Saya akan menggunakan mekanisme bahasa sederhana, yang meliputi:
Sebagai contoh, kami memiliki 2 kelas utama: Mahasiswa dan Universitas. Siswa memiliki IPK yang hanya dapat diakses oleh universitas. Ini kodenya:
sumber
Saya menduga itu ada hubungannya dengan model kompilasi C # - membangun IL kompilasi JIT yang saat runtime. yaitu: alasan yang sama bahwa obat generik C # pada dasarnya berbeda dengan obat generik C ++.
sumber
Anda dapat merahasiakannya dan menggunakan fungsi refleksi untuk memanggil. Kerangka uji dapat melakukan ini jika Anda memintanya untuk menguji fungsi pribadi
sumber
Saya biasa menggunakan teman, dan saya pikir itu bukan pelanggaran OOP atau tanda cacat desain apa pun. Ada beberapa tempat yang merupakan cara paling efisien untuk tujuan yang tepat dengan jumlah kode paling sedikit.
Salah satu contoh nyata adalah ketika membuat rakitan antarmuka yang menyediakan antarmuka komunikasi ke beberapa perangkat lunak lain. Secara umum ada beberapa kelas kelas berat yang menangani kompleksitas protokol dan kekhasan rekan, dan menyediakan model penghubung / baca / tulis / penerus / relatif yang relatif sederhana yang melibatkan pengiriman pesan dan pemberitahuan antara aplikasi klien dan perakitan. Pesan-pesan / notifikasi tersebut harus dibungkus dalam kelas. Atribut umumnya perlu dimanipulasi oleh perangkat lunak protokol karena pembuatnya, tetapi banyak hal yang harus tetap hanya-baca untuk dunia luar.
Sangat konyol untuk menyatakan bahwa ini merupakan pelanggaran OOP untuk kelas protokol / "pembuat" untuk memiliki akses intim ke semua kelas yang dibuat - kelas pembuat harus menggigit setiap bit data di jalan. Apa yang saya temukan paling penting adalah untuk meminimalkan semua baris BS tambahan kode yang biasanya mengarah ke model "OOP for Sake". Spaghetti ekstra hanya membuat lebih banyak bug.
Apakah orang tahu bahwa Anda dapat menerapkan kata kunci internal di tingkat atribut, properti, dan metode? Ini bukan hanya untuk deklarasi kelas tingkat atas (meskipun sebagian besar contoh tampaknya menunjukkan itu.)
Jika Anda memiliki kelas C ++ yang menggunakan kata kunci teman, dan ingin meniru itu dalam kelas C #: 1. mendeklarasikan kelas C # publik 2. mendeklarasikan semua atribut / properti / metode yang dilindungi dalam C ++ dan karenanya dapat diakses oleh teman-teman sebagai internal di C # 3. buat properti hanya baca untuk akses publik ke semua atribut dan properti internal
Saya setuju itu tidak 100% sama dengan teman, dan unit test adalah contoh yang sangat berharga dari kebutuhan akan sesuatu seperti teman (seperti juga kode logging log analyzer). Namun internal memberikan eksposur ke kelas yang ingin Anda ekspos, dan [InternalVisibleTo ()] menangani sisanya - sepertinya ia dilahirkan khusus untuk unit test.
Sejauh teman "menjadi lebih baik karena Anda dapat secara eksplisit mengontrol kelas mana yang memiliki akses" - apa gerangan yang dilakukan sekelompok tersangka kelas jahat di majelis yang sama? Partisi majelis Anda!
sumber
Persahabatan dapat disimulasikan dengan memisahkan antarmuka dan implementasi. Idenya adalah: " Membutuhkan contoh nyata tetapi membatasi akses konstruksi dari contoh itu ".
Sebagai contoh
Terlepas dari kenyataan bahwa
ItsMeYourFriend()
hanyaFriend
kelas publik yang dapat mengaksesnya, karena tidak ada orang lain yang bisa mendapatkan contoh nyata dariFriend
kelas tersebut. Ini memiliki konstruktor pribadi, sedangkanNew()
metode pabrik mengembalikan antarmuka.Lihat artikel saya Teman dan anggota antarmuka internal tanpa biaya dengan pengkodean ke antarmuka untuk detail.
sumber
Beberapa orang menyarankan bahwa hal-hal dapat di luar kendali dengan menggunakan teman. Saya setuju, tapi itu tidak mengurangi kegunaannya. Saya tidak yakin bahwa teman tentu saja menyakiti paradigma OO lebih daripada membuat semua anggota kelas Anda publik. Tentu saja bahasa akan memungkinkan Anda untuk membuat semua anggota Anda menjadi publik, tetapi itu adalah programmer yang disiplin yang menghindari jenis pola desain itu. Demikian juga programmer yang disiplin akan menggunakan teman untuk kasus-kasus tertentu yang masuk akal. Saya merasa internal terlalu banyak mengungkapkan dalam beberapa kasus. Mengapa mengekspos kelas atau metode untuk semua yang ada di majelis?
Saya memiliki halaman ASP.NET yang mewarisi halaman dasar saya sendiri, yang pada gilirannya mewarisi System.Web.UI.Page. Di halaman ini, saya memiliki beberapa kode yang menangani pelaporan kesalahan pengguna akhir untuk aplikasi dalam metode yang dilindungi
Sekarang, saya memiliki kontrol pengguna yang terdapat di halaman. Saya ingin kontrol pengguna dapat memanggil metode pelaporan kesalahan di halaman.
Tidak dapat melakukannya jika metode ReportError dilindungi. Saya bisa membuatnya internal, tetapi terkena kode apa pun di majelis. Saya hanya ingin itu terkena elemen UI yang merupakan bagian dari halaman saat ini (termasuk kontrol anak). Lebih khusus, saya ingin kelas kontrol basis saya untuk menentukan metode pelaporan kesalahan yang sama persis, dan cukup memanggil metode di halaman dasar.
Saya percaya bahwa sesuatu seperti teman dapat berguna dan diimplementasikan dalam bahasa tanpa membuat bahasa kurang "OO" seperti, mungkin sebagai atribut, sehingga Anda dapat memiliki kelas atau metode menjadi teman untuk kelas atau metode tertentu, yang memungkinkan pengembang untuk memberikan akses spesifik. Mungkin sesuatu seperti ... (kode semu)
Dalam kasus contoh saya sebelumnya mungkin memiliki sesuatu seperti berikut (orang dapat berdebat semantik, tapi saya hanya mencoba untuk menyampaikan ide):
Seperti yang saya lihat, konsep teman tidak memiliki risiko lebih dari itu daripada membuat sesuatu menjadi publik, atau menciptakan metode atau properti publik untuk mengakses anggota. Jika ada teman yang memungkinkan tingkat rincian aksesibilitas data yang lain dan memungkinkan Anda untuk mempersempit aksesibilitas itu daripada meluaskannya dengan internal atau publik.
sumber
Jika Anda bekerja dengan C ++ dan Anda menemukan diri Anda menggunakan kata kunci teman, itu adalah indikasi yang sangat kuat, bahwa Anda memiliki masalah desain, karena mengapa kelas perlu mengakses anggota pribadi dari kelas lain ??
sumber
Bsd
Dinyatakan bahwa, teman-teman menyakiti OOness murni. Yang saya setuju.
Juga dinyatakan bahwa teman-teman membantu enkapsulasi, yang juga saya setujui.
Saya pikir persahabatan harus ditambahkan ke metodologi OO, tetapi tidak cukup seperti di C ++. Saya ingin memiliki beberapa bidang / metode yang dapat diakses oleh kelas teman saya, tetapi saya TIDAK ingin mereka mengakses SEMUA bidang / metode saya. Seperti dalam kehidupan nyata, saya akan membiarkan teman-teman saya mengakses lemari es pribadi saya tetapi saya tidak akan membiarkan mereka mengakses rekening bank saya.
Seseorang dapat mengimplementasikan itu sebagai diikuti
Itu tentu saja akan menghasilkan peringatan kompiler, dan akan melukai intellisense. Tapi itu akan berhasil.
Sebagai catatan, saya berpikir bahwa seorang programmer yang percaya diri harus melakukan unit pengujian tanpa mengakses anggota pribadi. ini cukup di luar ruang lingkup, tetapi cobalah membaca tentang TDD. namun, jika Anda masih ingin melakukannya (memiliki c ++ seperti teman) coba sesuatu seperti
jadi Anda menulis semua kode Anda tanpa mendefinisikan UNIT_TESTING dan ketika Anda ingin melakukan pengujian unit Anda menambahkan #define UNIT_TESTING ke baris pertama file (dan menulis semua kode yang melakukan pengujian unit di bawah #jika UNIT_TESTING). Itu harus ditangani dengan hati-hati.
Karena saya pikir pengujian unit adalah contoh buruk untuk penggunaan teman, saya akan memberikan contoh mengapa saya pikir teman bisa baik. Misalkan Anda memiliki sistem melanggar (kelas). Dengan menggunakan, sistem melanggar usang dan perlu direnovasi. Sekarang, Anda ingin hanya mekanik berlisensi yang akan memperbaikinya. Untuk membuat contoh yang kurang sepele saya akan mengatakan bahwa montir akan menggunakan obeng pribadinya untuk memperbaikinya. Itu sebabnya kelas mekanik harus menjadi teman kelas breakingSystem.
sumber
c.MyMethod((FriendClass) null, 5.5, 3)
dari kelas mana pun.Persahabatan juga dapat disimulasikan dengan menggunakan "agen" - beberapa kelas batin. Pertimbangkan contoh berikut:
Itu bisa jauh lebih sederhana ketika digunakan untuk akses hanya ke anggota statis. Manfaat untuk implementasi semacam itu adalah bahwa semua tipe dideklarasikan di dalam lingkup kelas pertemanan dan, tidak seperti antarmuka, itu memungkinkan anggota statis untuk diakses.
sumber