Berbagai Warisan dalam C #

212

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:

  1. Tulis kelas baru yang diwarisi dari komponen dan mengimplementasikan antarmuka kelas TextTcpClient menggunakan instance dari kelas itu sendiri seperti yang ditunjukkan dengan FirstAndSecond.

  2. 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.

Martin
sumber
Sejauh ini bukan sekadar pewarisan berganda, bagaimana hal itu tidak rumit?
harpo
Berpikir tentang metode ekstensi baru dalam 3,5 dan cara kerjanya (pembuatan panggilan anggota statis), ini mungkin salah satu dari evolusi bahasa NET berikutnya.
Larry
Terkadang saya bertanya-tanya mengapa orang tidak hanya melakukan ... kelas A: kelas B: kelas C?
Chibueze Opata
@NazarMerza: Tautan telah berubah. Sekarang: Masalah dengan Berbagai Warisan .
Craig McQueen
9
Jangan biarkan propaganda menipu Anda. Contoh Anda sendiri menunjukkan bahwa pewarisan berganda berguna dan antarmuka hanyalah solusi untuk kekurangan itu
Kemal Erdogan

Jawaban:

125

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.

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 :(

IanNorton
sumber
39
CLR tidak mendukung beberapa warisan implementasi, hanya warisan multi antarmuka (yang juga didukung dalam C #).
Jordão
4
@ Jordão: Demi kelengkapan: adalah mungkin bagi kompiler untuk membuat MI untuk tipenya di CLR. Itu memang memiliki peringatan itu, itu tidak sesuai CLS misalnya. Untuk informasi lebih lanjut lihat ini (2004) Artikel blogs.msdn.com/b/csharpfaq/archive/2004/03/07/...
dvdvorle
2
@MrHappy: Artikel yang sangat menarik. Saya sebenarnya telah menyelidiki beberapa cara Komposisi Sifat untuk C #, lihatlah.
Jordão
10
@ MandeepJanjua Saya tidak mengklaim hal seperti itu, saya katakan 'boleh mengenalkannya' Faktanya tetap bahwa standar ECMA CLR memang menyediakan mesin IL untuk pewarisan berganda, hanya saja tidak ada yang memanfaatkan sepenuhnya.
IanNorton
4
FYI multiple inheritance tidak buruk, dan tidak membuat kode menjadi rumit. Kupikir aku akan menyebutkannya.
Dmitri Nesteruk
214

Pertimbangkan hanya menggunakan komposisi alih-alih mencoba mensimulasikan Multiple Inheritance. Anda dapat menggunakan Antarmuka untuk menentukan kelas apa yang membentuk komposisi, misalnya: ISteerablemenyiratkan properti tipe SteeringWheel, IBrakablemenyiratkan properti tipe BrakePedal, 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:

public interface ISteerable { SteeringWheel wheel { get; set; } }

public interface IBrakable { BrakePedal brake { get; set; } }

public class Vehicle : ISteerable, IBrakable
{
    public SteeringWheel wheel { get; set; }

    public BrakePedal brake { get; set; }

    public Vehicle() { wheel = new SteeringWheel(); brake = new BrakePedal(); }
}

public static class SteeringExtensions
{
    public static void SteerLeft(this ISteerable vehicle)
    {
        vehicle.wheel.SteerLeft();
    }
}

public static class BrakeExtensions
{
    public static void Stop(this IBrakable vehicle)
    {
        vehicle.brake.ApplyUntilStop();
    }
}


public class Main
{
    Vehicle myCar = new Vehicle();

    public void main()
    {
        myCar.SteerLeft();
        myCar.Stop();
    }
}
Chris Wenham
sumber
13
Itulah intinya - ide seperti ini akan memudahkan komposisi.
Jon Skeet
9
Ya, tetapi ada kasus penggunaan di mana Anda benar-benar membutuhkan metode sebagai bagian dari objek utama
David Pierre
9
Sayangnya data variabel anggota tidak dapat diakses dalam metode ekstensi sehingga Anda harus mengeksposnya sebagai internal atau (ug) publik, meskipun saya pikir komposisi berdasarkan kontrak adalah cara terbaik untuk menyelesaikan banyak warisan.
cfeduke
4
Jawaban yang sangat bagus! Ringkas, mudah dimengerti, ilustrasi yang sangat berguna. Terima kasih!
AJ.
3
Kami mungkin ingin memeriksa apakah myCarkemudi sudah selesai sebelum memanggil Stop. Mungkin berguling jika Stopditerapkan saat myCarberada dalam kecepatan yang berlebihan. : D
Devraj Gadhavi
16

Saya membuat C-post-compiler yang memungkinkan hal semacam ini:

using NRoles;

public interface IFirst { void FirstMethod(); }
public interface ISecond { void SecondMethod(); }

public class RFirst : IFirst, Role {
  public void FirstMethod() { Console.WriteLine("First"); }
}

public class RSecond : ISecond, Role {
  public void SecondMethod() { Console.WriteLine("Second"); }
}

public class FirstAndSecond : Does<RFirst>, Does<RSecond> { }

Anda bisa menjalankan post-compiler sebagai post-build-event Visual Studio:

C: \ some_path \ nroles-v0.1.0-bin \ nutate.exe "$ (TargetPath)"

Di majelis yang sama Anda menggunakannya seperti ini:

var fas = new FirstAndSecond();
fas.As<RFirst>().FirstMethod();
fas.As<RSecond>().SecondMethod();

Di majelis lain Anda menggunakannya seperti ini:

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();
Jordão
sumber
6

Anda bisa memiliki satu kelas dasar abstrak yang mengimplementasikan IFirst dan ISecond, dan kemudian mewarisi dari basis itu saja.

Joel Coehoorn
sumber
Ini mungkin solusi terbaik, tetapi belum tentu ide terbaik: p
leppie
1
tidakkah Anda masih harus mengedit kelas abstrak saat Anda menambahkan metode ke interafces?
Rik
Rik: bagaimana malasnya kamu, padahal kamu hanya perlu melakukan ini sekali saja?
leppie
3
@ leppie - "Setiap kali saya menambahkan metode ke salah satu antarmuka saya perlu mengubah kelas FirstAndSecond juga." Bagian dari pertanyaan awal ini tidak dibahas oleh solusi ini, bukan?
Rik
2
Anda harus mengedit kelas abstrak, tetapi Anda TIDAK harus mengedit kelas lain yang bergantung padanya. Uang berhenti di sana, daripada terus mengalir ke seluruh koleksi kelas.
Joel Coehoorn
3

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 ... :)

Thomas Hansen
sumber
Mungkinkah memiliki banyak pewarisan dan sementara memiliki upcast dan downcast tetap mempertahankan identitas? Solusi yang saya tahu untuk masalah pewarisan berganda berkisar pada memiliki gips yang tidak melestarikan identitas (jika myFoobertipe Foo, yang mewarisi dari Moodan Goo, keduanya mewarisi dari Boo, kemudian (Boo)(Moo)myFoodan (Boo)(Goo)myFootidak akan setara). Apakah Anda mengetahui adanya pendekatan pelestarian identitas?
supercat
1
Anda dapat menggunakan web.archive.org atau yang serupa untuk mengikuti tautan, tetapi ternyata ini merupakan diskusi yang lebih rinci tentang solusi yang ditawarkan dalam pertanyaan awal di sini.
MikeBeaton
2

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.

hydrix
sumber
2

Dengan C # 8 sekarang Anda secara praktis memiliki banyak warisan melalui implementasi default dari anggota antarmuka:

interface ILogger
{
    void Log(LogLevel level, string message);
    void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); // New overload
}

class ConsoleLogger : ILogger
{
    public void Log(LogLevel level, string message) { ... }
    // Log(Exception) gets default implementation
}
Ludmil Tinkov
sumber
4
Ya, tetapi perhatikan bahwa, di atas, Anda tidak akan dapat melakukannya new ConsoleLogger().Log(someEception)- itu tidak akan berfungsi, Anda harus secara eksplisit melemparkan objek Anda ILoggerke menggunakan metode antarmuka standar. Jadi kegunaannya agak terbatas.
Dmitri Nesteruk
1

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.

public interface IFirst {}
public interface ISecond {}

public class FirstAndSecond : IFirst, ISecond
{
}

public static MultipleInheritenceExtensions
{
  public static void First(this IFirst theFirst)
  {
    Console.WriteLine("First");
  }

  public static void Second(this ISecond theSecond)
  {
    Console.WriteLine("Second");
  }
}

///

public void Test()
{
  FirstAndSecond fas = new FirstAndSecond();
  fas.First();
  fas.Second();
}

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.

Amy B
sumber
1

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:

 namespace OOP
 {
     class Program
     {
         static void Main(string[] args)
         {
             Child somechild = new Child();
             somechild.DoHomeWork();
             somechild.CheckingAround();
             Console.ReadLine();
         }
     }

     public class Father 
     {
         public Father() { }
         public void Work()
         {
             Console.WriteLine("working...");
         }
         public void Moonlight()
         {
             Console.WriteLine("moonlighting...");
         }
     }


     public class Mother 
     {
         public Mother() { }
         public void Cook()
         {
             Console.WriteLine("cooking...");
         }
         public void Clean()
         {
             Console.WriteLine("cleaning...");
         }
     }


     public class Child 
     {
         public Father MyFather { get; set; }
         public Mother MyMother { get; set; }

         public Child()
         {
             MyFather = new Father();
             MyMother = new Mother();
         }

         public void GoToSchool()
         {
             Console.WriteLine("go to school...");
         }
         public void DoHomeWork()
         {
             Console.WriteLine("doing homework...");
         }
         public void CheckingAround()
         {
             MyFather.Work();
             MyMother.Cook();
         }
     }


 }

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.

Yogi
sumber
2
Saya tidak setuju dengan paragraf pertama. Anda hanya menambahkan tanda tangan dari metode yang Anda inginkan di kelas SETIAP ke antarmuka. Tetapi Anda dapat menambahkan metode tambahan sebanyak mungkin ke kelas apa pun yang Anda inginkan. Juga, ada klik kanan, ekstrak antarmuka yang membuat pekerjaan mengekstraksi antarmuka menjadi sederhana. Akhirnya, contoh Anda bukanlah warisan (berganda atau tidak), namun merupakan contoh komposisi yang bagus. Sayangnya, itu akan lebih bermanfaat jika Anda menggunakan antarmuka untuk juga menunjukkan DI / IOC menggunakan konstruktor / injeksi properti. Sementara saya tidak akan memilih saya pikir itu bukan jawaban yang baik maaf.
Francis Rodgers
1
Melihat kembali utas ini setahun kemudian, saya setuju Anda dapat menambahkan metode sebanyak yang Anda inginkan di kelas tanpa menambahkan tanda tangan di antarmuka, namun, ini akan membuat antarmuka tidak lengkap. Selain itu, saya tidak dapat menemukan klik kanan - ekstrak antarmuka di IDE saya, mungkin saya kehilangan sesuatu. Tapi, kekhawatiran saya yang lebih besar adalah, ketika Anda menentukan tanda tangan di antarmuka, kelas yang mewarisi harus mengimplementasikan tanda tangan itu. Saya pikir ini adalah pekerjaan ganda dan dapat menyebabkan duplikasi kode.
Yogi
INTERFACE EKSTRAK: klik kanan di atas tanda tangan kelas, lalu ekstrak antarmuka ... dalam VS2015 proses yang sama kecuali Anda harus mengklik kanan, lalu pilih Quick Actions and Refactorings...ini adalah harus tahu, itu akan menghemat banyak waktu
Chef_Code
1

Kita 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?)

class Program
{
    static void Main(string[] args)
    {
        human me = new human();
        me.legs = 2;
        me.lfType = "Human";
        me.name = "Paul";
        Console.WriteLine(me.name);
    }
}

public abstract class lifeform
{
    public string lfType { get; set; }
}

public abstract class mammal : lifeform 
{
    public int legs { get; set; }
}

public class human : mammal
{
    public string name { get; set; }
}

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 ...

class Program
{
    static void Main(string[] args)
    {
        fish shark = new fish();
        shark.size = "large";
        shark.lfType = "Fish";
        shark.name = "Jaws";
        Console.WriteLine(shark.name);
        human me = new human();
        me.legs = 2;
        me.lfType = "Human";
        me.name = "Paul";
        Console.WriteLine(me.name);
    }
}

public abstract class lifeform
{
    public string lfType { get; set; }
}

public abstract class mammal : lifeform 
{
    public int legs { get; set; }
}

public class human : mammal
{
    public string name { get; set; }
}

public class aquatic : lifeform
{
    public string size { get; set; }
}

public class fish : aquatic
{
    public string name { get; set; }
}
Paul
sumber
0

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.

tloach
sumber
8
Tolong jangan menilai MI dengan C ++, itu seperti menilai OOP dengan PHP atau mobil oleh Pintos. Masalah itu mudah dipecahkan: di Eiffel, ketika Anda mewarisi dari suatu kelas, Anda juga harus menentukan metode mana yang ingin Anda warisi dan Anda dapat mengubah nama mereka. Tidak ada ambiguitas di sana dan juga tidak ada kejutan.
Jörg W Mittag
2
@ MP: tidak, Eiffel menyediakan beberapa warisan implementasi yang sebenarnya. Mengganti nama tidak berarti kehilangan rantai warisan, juga tidak akan kehilangan kastabilitas kelas.
Abel
0

Jika X mewarisi dari Y, itu memiliki dua efek yang agak ortogonal:

  1. Y akan memberikan fungsionalitas default untuk X, jadi kode untuk X hanya harus menyertakan hal-hal yang berbeda dari Y.
  2. Hampir di mana saja Y diharapkan, sebuah X dapat digunakan sebagai gantinya.

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).

supercat
sumber
0

saya tahu saya tahu meskipun tidak diizinkan dan seterusnya, kadang-kadang Anda benar-benar membutuhkannya untuk mereka:

class a {}
class b : a {}
class c : b {}

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

Ariel
sumber
1
Contoh Anda tidak menggambarkan banyak pewarisan, jadi masalah apa yang Anda coba selesaikan? Contoh multiple inheritance yang sebenarnya akan ditampilkan class a : b, c(menerapkan setiap lubang kontrak yang diperlukan). Mungkin contoh Anda terlalu disederhanakan?
M.Babcock
0

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, FirstAndSecondpendekatan, seperti yang disajikan dalam pertanyaan. Saya mengurangi kode sampel menjadi IFirst, karena polanya tetap sama terlepas dari jumlah antarmuka / kelas dasar MI.

Mari kita asumsikan, bahwa dengan MI Firstdan Secondkeduanya akan berasal dari kelas dasar yang sama BaseClass, hanya menggunakan elemen antarmuka publik dariBaseClass

Ini dapat diungkapkan, dengan menambahkan referensi wadah ke BaseClassdalam Firstdan Secondimplementasi:

class First : IFirst {
  private BaseClass ContainerInstance;
  First(BaseClass container) { ContainerInstance = container; }
  public void FirstMethod() { Console.WriteLine("First"); ContainerInstance.DoStuff(); } 
}
...

Hal-hal menjadi lebih rumit, ketika elemen antarmuka yang dilindungi dari BaseClassdireferensikan atau kapan Firstdan Secondakan menjadi kelas abstrak di MI, membutuhkan subclass mereka untuk mengimplementasikan beberapa bagian abstrak.

class BaseClass {
  protected void DoStuff();
}

abstract class First : IFirst {
  public void FirstMethod() { DoStuff(); DoSubClassStuff(); }
  protected abstract void DoStuff(); // base class reference in MI
  protected abstract void DoSubClassStuff(); // sub class responsibility
}

C # memungkinkan kelas bersarang untuk mengakses elemen yang dilindungi / pribadi dari kelas yang mengandungnya, sehingga ini dapat digunakan untuk menautkan bit abstrak dari Firstimplementasi.

class FirstAndSecond : BaseClass, IFirst, ISecond {
  // link interface
  private class PartFirst : First {
    private FirstAndSecond ContainerInstance;
    public PartFirst(FirstAndSecond container) {
      ContainerInstance = container;
    }
    // forwarded references to emulate access as it would be with MI
    protected override void DoStuff() { ContainerInstance.DoStuff(); }
    protected override void DoSubClassStuff() { ContainerInstance.DoSubClassStuff(); }
  }
  private IFirst partFirstInstance; // composition object
  public FirstMethod() { partFirstInstance.FirstMethod(); } // forwarded implementation
  public FirstAndSecond() {
    partFirstInstance = new PartFirst(this); // composition in constructor
  }
  // same stuff for Second
  //...
  // implementation of DoSubClassStuff
  private void DoSubClassStuff() { Console.WriteLine("Private method accessed"); }
}

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.

grek40
sumber
0

Ini sejalan dengan jawaban Lawrence Wenham, tetapi tergantung pada kasus penggunaan Anda, ini mungkin atau mungkin bukan peningkatan - Anda tidak perlu setter.

public interface IPerson {
  int GetAge();
  string GetName();
}

public interface IGetPerson {
  IPerson GetPerson();
}

public static class IGetPersonAdditions {
  public static int GetAgeViaPerson(this IGetPerson getPerson) { // I prefer to have the "ViaPerson" in the name in case the object has another Age property.
    IPerson person = getPerson.GetPersion();
    return person.GetAge();
  }
  public static string GetNameViaPerson(this IGetPerson getPerson) {
    return getPerson.GetPerson().GetName();
  }
}

public class Person: IPerson, IGetPerson {
  private int Age {get;set;}
  private string Name {get;set;}
  public IPerson GetPerson() {
    return this;
  }
  public int GetAge() {  return Age; }
  public string GetName() { return Name; }
}

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.

William Jockusch
sumber
0

Ini sekarang dimungkinkan melalui partialkelas, masing-masing dapat mewarisi kelas sendiri, membuat objek akhir mewarisi semua kelas dasar. Anda dapat mempelajari lebih lanjut di sini .

Nekuś
sumber