Karena multiple inheritance buruk (itu membuat sumber lebih rumit) C # tidak menyediakan pola seperti itu secara langsung. Tetapi terkadang akan sangat membantu jika memiliki kemampuan ini.
Sebagai contoh, saya dapat mengimplementasikan pola pewarisan berganda yang hilang menggunakan antarmuka dan tiga kelas seperti itu:
public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }
public class First:IFirst
{
public void FirstMethod() { Console.WriteLine("First"); }
}
public class Second:ISecond
{
public void SecondMethod() { Console.WriteLine("Second"); }
}
public class FirstAndSecond: IFirst, ISecond
{
First first = new First();
Second second = new Second();
public void FirstMethod() { first.FirstMethod(); }
public void SecondMethod() { second.SecondMethod(); }
}
Setiap kali saya menambahkan metode ke salah satu antarmuka saya perlu mengubah kelas FirstAndSecond juga.
Apakah ada cara untuk menyuntikkan beberapa kelas yang ada ke dalam satu kelas baru seperti itu mungkin di C ++?
Mungkin ada solusi menggunakan semacam pembuatan kode?
Atau mungkin terlihat seperti ini (sintaksis c # imajiner):
public class FirstAndSecond: IFirst from First, ISecond from Second
{ }
Sehingga tidak perlu memperbarui kelas FirstAndSecond ketika saya memodifikasi salah satu antarmuka.
EDIT
Mungkin akan lebih baik untuk mempertimbangkan contoh praktis:
Anda memiliki kelas yang sudah ada (mis. Klien TCP berbasis teks yang berbasis pada ITextTcpClient) yang sudah Anda gunakan di lokasi berbeda di dalam proyek Anda. Sekarang Anda merasa perlu membuat komponen kelas Anda agar mudah diakses oleh pengembang bentuk windows.
Sejauh yang saya tahu Anda saat ini memiliki dua cara untuk melakukan ini:
Tulis kelas baru yang diwarisi dari komponen dan mengimplementasikan antarmuka kelas TextTcpClient menggunakan instance dari kelas itu sendiri seperti yang ditunjukkan dengan FirstAndSecond.
Tulis kelas baru yang diwarisi dari TextTcpClient dan entah bagaimana mengimplementasikan IComponent (belum benar-benar mencoba ini).
Dalam kedua kasus, Anda perlu melakukan pekerjaan per metode dan bukan per kelas. Karena Anda tahu bahwa kita akan memerlukan semua metode TextTcpClient dan Component, itu akan menjadi solusi termudah untuk hanya menggabungkan keduanya menjadi satu kelas.
Untuk menghindari konflik ini dapat dilakukan oleh pembuatan kode di mana hasilnya dapat diubah setelahnya tetapi mengetik ini dengan tangan adalah rasa sakit yang murni di pantat.
sumber
Jawaban:
C # dan .net CLR belum mengimplementasikan MI karena mereka belum menyimpulkan bagaimana itu akan beroperasi antara C #, VB.net dan bahasa lainnya, bukan karena "itu akan membuat sumber menjadi lebih kompleks"
MI adalah konsep yang berguna, pertanyaan-pertanyaan yang tidak dijawab adalah seperti: - "Apa yang Anda lakukan ketika Anda memiliki beberapa kelas dasar umum dalam kacamata super yang berbeda?
Perl adalah satu-satunya bahasa yang pernah saya gunakan di mana MI bekerja dan bekerja dengan baik. Net mungkin mengenalkannya suatu hari tetapi belum, CLR sudah mendukung MI tapi seperti yang saya katakan, belum ada konstruksi bahasa untuk itu di luar itu.
Sampai saat itu Anda terjebak dengan objek Proxy dan beberapa Antarmuka sebagai gantinya :(
sumber
Pertimbangkan hanya menggunakan komposisi alih-alih mencoba mensimulasikan Multiple Inheritance. Anda dapat menggunakan Antarmuka untuk menentukan kelas apa yang membentuk komposisi, misalnya:
ISteerable
menyiratkan properti tipeSteeringWheel
,IBrakable
menyiratkan properti tipeBrakePedal
, dll.Setelah Anda selesai melakukannya, Anda dapat menggunakan fitur Metode Ekstensi yang ditambahkan ke C # 3.0 untuk lebih menyederhanakan metode panggilan pada properti tersirat tersebut, misalnya:
sumber
myCar
kemudi sudah selesai sebelum memanggilStop
. Mungkin berguling jikaStop
diterapkan saatmyCar
berada dalam kecepatan yang berlebihan. : DSaya membuat C-post-compiler yang memungkinkan hal semacam ini:
Anda bisa menjalankan post-compiler sebagai post-build-event Visual Studio:
Di majelis yang sama Anda menggunakannya seperti ini:
Di majelis lain Anda menggunakannya seperti ini:
sumber
Anda bisa memiliki satu kelas dasar abstrak yang mengimplementasikan IFirst dan ISecond, dan kemudian mewarisi dari basis itu saja.
sumber
MI TIDAK buruk, semua orang yang (serius) menggunakannya MENYUKAINYA dan itu TIDAK menyulitkan kode! Setidaknya tidak lebih dari konstruksi lain dapat menyulitkan kode. Kode buruk adalah kode buruk terlepas dari apakah MI ada di dalam gambar atau tidak.
Bagaimanapun, saya punya solusi kecil yang bagus untuk Multiple Inheritance yang ingin saya bagikan, yaitu di; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog atau Anda dapat mengikuti tautan di sig ... :)
sumber
myFoo
bertipeFoo
, yang mewarisi dariMoo
danGoo
, keduanya mewarisi dariBoo
, kemudian(Boo)(Moo)myFoo
dan(Boo)(Goo)myFoo
tidak akan setara). Apakah Anda mengetahui adanya pendekatan pelestarian identitas?Dalam implementasi saya sendiri, saya menemukan bahwa menggunakan kelas / antarmuka untuk MI, meskipun "bentuk yang baik", cenderung menjadi masalah besar karena Anda perlu mengatur semua pewarisan berganda untuk hanya beberapa pemanggilan fungsi yang diperlukan, dan dalam kasus saya, perlu dilakukan puluhan kali secara berlebihan.
Alih-alih, lebih mudah membuat "fungsi yang memanggil fungsi yang memanggil fungsi" statis dalam varietas modular berbeda sebagai semacam penggantian OOP. Solusi yang saya kerjakan adalah "sistem mantra" untuk RPG di mana efek perlu berat campuran-dan-pertandingan fungsi menelepon untuk memberi ekstrim berbagai mantra tanpa kode menulis ulang, seperti contoh tampaknya menunjukkan.
Sebagian besar fungsi sekarang bisa statis karena saya tidak perlu contoh untuk logika ejaan, sedangkan kelas warisan bahkan tidak dapat menggunakan kata kunci virtual atau abstrak sementara statis. Antarmuka tidak dapat menggunakannya sama sekali.
Pengkodean tampaknya lebih cepat dan lebih bersih dengan cara ini IMO. Jika Anda hanya melakukan fungsi, dan tidak membutuhkan properti yang diwarisi , gunakan fungsi.
sumber
Dengan C # 8 sekarang Anda secara praktis memiliki banyak warisan melalui implementasi default dari anggota antarmuka:
sumber
new ConsoleLogger().Log(someEception)
- itu tidak akan berfungsi, Anda harus secara eksplisit melemparkan objek AndaILogger
ke menggunakan metode antarmuka standar. Jadi kegunaannya agak terbatas.Jika Anda dapat hidup dengan batasan bahwa metode IFirst dan ISecond hanya harus berinteraksi dengan kontrak IFirst dan ISecond (seperti dalam contoh Anda) ... Anda dapat melakukan apa yang Anda minta dengan metode ekstensi. Dalam praktiknya, ini jarang terjadi.
///
Jadi ide dasarnya adalah Anda mendefinisikan implementasi yang diperlukan di antarmuka ... hal-hal yang diperlukan ini harus mendukung implementasi fleksibel dalam metode ekstensi. Kapan saja Anda perlu "menambahkan metode ke antarmuka", alih-alih Anda menambahkan metode ekstensi.
sumber
Ya menggunakan Interface itu merepotkan karena setiap kali kita menambahkan metode di kelas kita harus menambahkan tanda tangan di antarmuka. Juga, bagaimana jika kita sudah memiliki kelas dengan banyak metode tetapi tidak memiliki antarmuka untuk itu? kita harus secara manual membuat Antarmuka untuk semua kelas yang ingin kita warisi. Dan yang terburuk adalah, kita harus mengimplementasikan semua metode di Interfaces di kelas anak jika kelas anak ingin diwariskan dari beberapa antarmuka.
Dengan mengikuti pola desain Facade kita dapat mensimulasikan pewarisan dari beberapa kelas menggunakan pengakses . Deklarasikan kelas sebagai properti dengan {get; set;} di dalam kelas yang perlu diwariskan dan semua properti publik dan metode berasal dari kelas itu, dan dalam konstruktor kelas anak instantiate kelas induk.
Sebagai contoh:
dengan struktur ini, kelas Anak akan memiliki akses ke semua metode dan properti Kelas Ayah dan Ibu, mensimulasikan banyak warisan, mewarisi turunan dari kelas induk. Tidak persis sama tetapi praktis.
sumber
Quick Actions and Refactorings...
ini adalah harus tahu, itu akan menghemat banyak waktuKita semua tampaknya menuju jalur antarmuka dengan ini, tetapi kemungkinan lain yang jelas, di sini, adalah untuk melakukan apa yang seharusnya dilakukan OOP, dan membangun pohon warisan Anda ... (bukankah ini desain kelas apa semua tentang?)
Struktur ini menyediakan blok kode yang dapat digunakan kembali dan, tentu saja, bagaimana seharusnya kode OOP ditulis?
Jika pendekatan khusus ini tidak cukup sesuai dengan tagihan, kita cukup membuat kelas baru berdasarkan objek yang diperlukan ...
sumber
Warisan berganda adalah salah satu dari hal-hal yang umumnya menyebabkan lebih banyak masalah daripada memecahkannya Dalam C ++ itu cocok dengan pola memberi Anda cukup tali untuk menggantung diri, tetapi Java dan C # telah memilih untuk mengambil rute yang lebih aman dengan tidak memberikan Anda opsi. Masalah terbesar adalah apa yang harus dilakukan jika Anda mewarisi beberapa kelas yang memiliki metode dengan tanda tangan yang sama yang tidak diimplementasikan oleh pewarisan. Metode kelas mana yang harus dipilih? Atau haruskah itu tidak dikompilasi? Pada umumnya ada cara lain untuk mengimplementasikan sebagian besar hal yang tidak bergantung pada multiple inheritance.
sumber
Jika X mewarisi dari Y, itu memiliki dua efek yang agak ortogonal:
Meskipun warisan menyediakan untuk kedua fitur, tidak sulit untuk membayangkan keadaan di mana salah satu dapat digunakan tanpa yang lain. Tidak ada .net bahasa yang saya tahu memiliki cara langsung untuk mengimplementasikan yang pertama tanpa yang kedua, meskipun orang bisa mendapatkan fungsionalitas seperti itu dengan mendefinisikan kelas dasar yang tidak pernah digunakan secara langsung, dan memiliki satu atau lebih kelas yang mewarisi langsung darinya tanpa menambahkan apa pun baru (kelas-kelas semacam itu dapat membagikan semua kode mereka, tetapi tidak akan dapat saling menggantikan). Namun, bahasa apa pun yang mendukung CLR akan memungkinkan penggunaan antarmuka yang menyediakan fitur antarmuka kedua (kemampuan penggantian) tanpa yang pertama (penggunaan kembali anggota).
sumber
saya tahu saya tahu meskipun tidak diizinkan dan seterusnya, kadang-kadang Anda benar-benar membutuhkannya untuk mereka:
seperti dalam kasus saya, saya ingin melakukan kelas ini b: Formulir (yep windows.forms) kelas c: b {}
menyebabkan setengah dari fungsi itu identik dan dengan antarmuka Anda harus menulis ulang semuanya
sumber
class a : b, c
(menerapkan setiap lubang kontrak yang diperlukan). Mungkin contoh Anda terlalu disederhanakan?Karena pertanyaan multiple inheritance (MI) muncul dari waktu ke waktu, saya ingin menambahkan pendekatan yang membahas beberapa masalah dengan pola komposisi.
Saya membangun atas
IFirst
,ISecond
,First
,Second
,FirstAndSecond
pendekatan, seperti yang disajikan dalam pertanyaan. Saya mengurangi kode sampel menjadiIFirst
, karena polanya tetap sama terlepas dari jumlah antarmuka / kelas dasar MI.Mari kita asumsikan, bahwa dengan MI
First
danSecond
keduanya akan berasal dari kelas dasar yang samaBaseClass
, hanya menggunakan elemen antarmuka publik dariBaseClass
Ini dapat diungkapkan, dengan menambahkan referensi wadah ke
BaseClass
dalamFirst
danSecond
implementasi:Hal-hal menjadi lebih rumit, ketika elemen antarmuka yang dilindungi dari
BaseClass
direferensikan atau kapanFirst
danSecond
akan menjadi kelas abstrak di MI, membutuhkan subclass mereka untuk mengimplementasikan beberapa bagian abstrak.C # memungkinkan kelas bersarang untuk mengakses elemen yang dilindungi / pribadi dari kelas yang mengandungnya, sehingga ini dapat digunakan untuk menautkan bit abstrak dari
First
implementasi.Ada beberapa pelat yang terlibat, tetapi jika implementasi FirstMethod dan SecondMethod yang sebenarnya cukup kompleks dan jumlah metode privat / terproteksi yang diakses cukup, maka pola ini dapat membantu mengatasi kekurangan pewarisan berganda.
sumber
Ini sejalan dengan jawaban Lawrence Wenham, tetapi tergantung pada kasus penggunaan Anda, ini mungkin atau mungkin bukan peningkatan - Anda tidak perlu setter.
Sekarang objek apa pun yang tahu cara mendapatkan seseorang dapat mengimplementasikan IGetPerson, dan secara otomatis akan memiliki metode GetAgeViaPerson () dan GetNameViaPerson (). Dari titik ini, pada dasarnya semua kode Person masuk ke IGetPerson, bukan ke IPerson, selain ivars baru, yang harus masuk ke keduanya. Dan dalam menggunakan kode seperti itu, Anda tidak perlu khawatir tentang apakah objek IGetPerson Anda sebenarnya adalah IPerson atau bukan.
sumber
Ini sekarang dimungkinkan melalui
partial
kelas, masing-masing dapat mewarisi kelas sendiri, membuat objek akhir mewarisi semua kelas dasar. Anda dapat mempelajari lebih lanjut di sini .sumber