C # 'da Çoklu Devralma


212

Çoklu kalıtım kötü olduğundan (kaynağı daha karmaşık hale getirir) C # böyle bir modeli doğrudan sağlamaz. Ancak bazen bu yeteneğe sahip olmak yardımcı olabilir.

Örneğin, arayüzleri ve bunun gibi üç sınıfı kullanarak eksik çoklu kalıtım modelini uygulayabiliyorum:

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(); }
}

Arabirimlerden birine bir yöntem eklediğimde, FirstAndSecond sınıfını da değiştirmem gerekiyor .

Birden fazla mevcut sınıfı C ++ 'da olduğu gibi yeni bir sınıfa enjekte etmenin bir yolu var mı?

Belki bir tür kod üretimi kullanan bir çözüm var mı?

Veya şu şekilde görünebilir (hayali c # sözdizimi):

public class FirstAndSecond: IFirst from First, ISecond from Second
{ }

Arabirimlerden birini değiştirdiğimde FirstAndSecond sınıfını güncellemeye gerek kalmayacak.


DÜZENLE

Belki de pratik bir örnek düşünmek daha iyi olur:

Projenizdeki farklı konumlarda zaten kullandığınız mevcut bir sınıfınız (örn. ITextTcpClient tabanlı metin tabanlı bir TCP istemciniz) var. Artık Windows form geliştiricileri için kolay erişilebilir olması için sınıfınızın bir bileşenini oluşturma gereğini hissediyorsunuz.

Bildiğim kadarıyla şu anda bunu yapmak için iki yol var:

  1. FirstAndSecond ile gösterildiği gibi sınıfın bir örneğini kullanarak, bileşenlerden devralınan ve TextTcpClient sınıfının arabirimini uygulayan yeni bir sınıf yazın.

  2. TextTcpClient'ten devralınan ve bir şekilde IComponent'i uygulayan yeni bir sınıf yazın (bunu henüz denemedim).

Her iki durumda da, sınıf başına değil, yöntem başına çalışma yapmanız gerekir. TextTcpClient ve Component'in tüm yöntemlerine ihtiyacımız olacağını bildiğiniz için, bu ikisini tek bir sınıfta birleştirmek en kolay çözüm olacaktır.

Çatışmalardan kaçınmak için bu, sonucun daha sonra değiştirilebileceği kod üretimi ile yapılabilir, ancak bunu elle yazmak eşek için saf bir acıdır.


Bu sadece kılık değiştirmiş bir çoklu miras olmadığı sürece, nasıl daha az karmaşıktır?
harpo

3.5'teki yeni uzantı yöntemlerini ve çalışma şeklini (statik üye çağrı oluşturma) düşünerek, bu bir sonraki .NET dil gelişiminden biri olabilir.
Larry

Bazen merak ediyorum neden insanlar sadece A sınıfı: B sınıfı: C sınıfı?
Chibueze Opata

@NazarMerza: Bağlantı değişti. Şimdi: Çoklu Kalıtım Sorunu .
Craig McQueen

9
Propagandanın sizi kandırmasına izin vermeyin. Örneğiniz, çoklu kalıtımın yararlı olduğunu ve arayüzlerin olmaması için sadece bir çözüm olduğunu gösterir
Kemal Erdoğan

Yanıtlar:


125

Çoklu kalıtım kötü olduğundan (kaynağı daha karmaşık hale getirir) C # böyle bir modeli doğrudan sağlamaz. Ancak bazen bu yeteneğe sahip olmak yardımcı olabilir.

C # ve .net CLR MI uygulamamıştır çünkü C #, VB.net ve diğer diller arasında nasıl etkileşime gireceği sonucuna varmamışlardır, çünkü "kaynağı daha karmaşık hale getirecektir"

MI yararlı bir kavramdır, cevaplanmamış sorular şöyledir: - "Farklı üst sınıflarda birden fazla ortak temel sınıfınız olduğunda ne yaparsınız?

Perl, MI'nın iyi çalıştığı ve çalıştığı yerde çalıştığım tek dildir. Net bir gün iyi tanıtabilir, ancak henüz değil, CLR zaten MI'yi destekliyor, ancak söylediğim gibi, bunun ötesinde henüz bir dil kurumu yok.

O zamana kadar Proxy nesneleri ve birden çok Arayüz ile sıkışıp kalırsınız :(


39
CLR çoklu uygulama devralma özelliğini desteklemez, yalnızca çoklu arabirim devralma özelliğini destekler (C # 'da da desteklenir).
Jordão

4
@ Jordão: Tamlık uğruna: derleyicilerin CLR'deki türleri için MI oluşturmaları mümkündür. Uyarıları var, örneğin CLS uyumlu değil. Daha fazla bilgi için bu (2004) makaleye bakın blogs.msdn.com/b/csharpfaq/archive/2004/03/07/…
dvdvorle

2
@MrHappy: Çok ilginç bir makale. Aslında ben C # için Trait Composition bir şekilde araştırdım , bir göz atın.
Jordão

10
@MandeepJanjua Ben böyle bir şey iddia etmedim, 'iyi tanıtabilir' dedim Gerçek şu ki ECMA standart CLR IL makine çoklu kalıtım için sağlar, sadece hiçbir şey tam olarak kullanmaz.
IanNorton

4
FYI çoklu kalıtım kötü değildir ve kodu karmaşık hale getirmez. Sadece bundan söz edeceğimi düşündüm.
Dmitri Nesteruk

214

Çoklu Devralmayı simüle etmek yerine sadece kompozisyon kullanmayı düşünün . Sen kompozisyon, örneğin makyaj hangi dersleri tanımlamak için arabirimleri kullanabilirsiniz: ISteerabletürden bir özellik ima SteeringWheel, IBrakabletüründe bir özellik ima BrakePedalvs.

Bunu yaptıktan sonra, ima edilen özellikler üzerindeki çağrı yöntemlerini daha da basitleştirmek için C # 3.0'a eklenen Uzantı Yöntemleri özelliğini kullanabilirsiniz , örneğin:

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();
    }
}

13
Ancak mesele bu - böyle bir fikir kompozisyonu kolaylaştıracaktır.
Jon Skeet

9
Evet, ancak ana nesnenin bir parçası olarak yöntemlere gerçekten ihtiyaç duyduğunuz kullanım örnekleri var
David Pierre

9
Ne yazık ki üye değişken verilerine genişletme yöntemlerinde erişilemez, bu yüzden bunları dahili veya (ug) genel olarak ortaya çıkarmak zorundasınız, ancak sözleşmeyle kompozisyonun birden fazla miras çözmenin en iyi yolu olduğunu düşünüyorum.
cfeduke

4
Mükemmel cevap! Özlü, anlaşılması kolay, çok kullanışlı illüstrasyon. Teşekkür ederim!
AJ.

3
myCarAramadan önce direksiyonu bırakıp bırakmadığını kontrol etmek isteyebiliriz Stop. Eğer üzerinde rulo olabilir Stopuygulamalı iken myCaraşırı hız içindedir. : D
Devraj Gadhavi

16

Bu tür bir şey sağlayan bir C # post-derleyici oluşturdum:

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> { }

Post-derleyiciyi bir Visual Studio post-build-event olarak çalıştırabilirsiniz:

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

Aynı montajda şu şekilde kullanırsınız:

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

Başka bir derlemede şu şekilde kullanırsınız:

var fas = new FirstAndSecond();
fas.FirstMethod();
fas.SecondMethod();

6

Hem IFirst hem de ISecond'ı uygulayan ve daha sonra sadece bu tabandan devralınan bir soyut temel sınıfınız olabilir.


Bu muhtemelen en iyi çözümdür, ancak mutlaka en iyi fikir değildir: p
leppie

1
tamsayılara yöntem eklerken soyut sınıfı düzenlemeniz gerekmez mi?
Rik

Rik: Bunu bir kez yapmak zorunda kaldığın zaman ne kadar tembelsin?
Lepi

3
@leppie - "Arabirimlerden birine her yöntem eklediğimde, FirstAndSecond sınıfını da değiştirmem gerekiyor." Orijinal sorunun bu kısmı bu çözüm tarafından ele alınmıyor, değil mi?
Rik

2
Soyut sınıfı düzenlemeniz gerekir, ancak ona bağlı olan diğer sınıfları düzenlemeniz GEREKMEZ. Kova, tüm sınıf koleksiyonuna adım atmaya devam etmek yerine orada durur.
Joel Coehoorn

3

MI kötü değil, (ciddi olarak) kullanan herkes onu SEVİYOR ve kodu karmaşık hale getirmiyor! En azından diğer yapılardan daha fazla kodu zorlaştırabilir. Yanlış kod, MI'nın resimde olup olmadığına bakılmaksızın hatalı koddur.

Her neyse, paylaşmak istediğim Çoklu Devralma için güzel bir çözüm buldum; http://ra-ajax.org/lsp-liskov-substitution-principle-to-be-or-not-to-be.blog ya da benim sig bağlantıyı takip edebilirsiniz ... :)


Birden fazla mirasa sahip olmak ve upcasts ve downcasts kimliklerini korumak mümkün mü? Birden çok kalıtım sorunlara biliyor çözeltileri kimlik koruyucu olmayan yayınları sahip etrafında döner (eğer myFootiptedir Foogelen devralır Moove Gooher ikisi de miras arasında den Boosonra (Boo)(Moo)myFoove (Boo)(Goo)myFooeş olmaz). Kimlik koruma yaklaşımlarının farkında mısınız?
supercat

1
Bağlantıyı takip etmek için web.archive.org veya benzerlerini kullanabilirsiniz, ancak buradaki orijinal soruda sunulan çözümün tam olarak daha ayrıntılı bir tartışması olduğu ortaya çıkıyor.
MikeBeaton

2

Kendi uygulamamda, MI için sınıflar / arayüzler kullanmanın, "iyi form" olmasına rağmen, sadece birkaç gerekli işlev çağrısı için tüm bu çoklu kalıtımları ayarlamanız gerektiğinden, karmaşıklık üzerinde büyük bir eğilim olduğunu gördüm ve benim durumumda, tam anlamıyla düzinelerce kez yapılması gerekiyordu.

Bunun yerine, farklı modüler çeşitlerde bir çeşit OOP değiştirme olarak statik "fonksiyonları çağıran fonksiyonlar" işlevlerini yapmak daha kolaydı. Üzerinde çalıştığım çözüm , örneklerin gösterdiği gibi, kodların yeniden yazılmadan çok çeşitli büyüler vermesi için çağrıların yoğun bir şekilde karıştırılması ve eşleştirilmesi gereken bir RPG için "büyü sistemi" idi .

Sınıf mirasının statik olsa bile sanal veya soyut anahtar sözcükleri kullanamamasına rağmen, işlevlerin çoğu artık statik olabilir. Arayüzler onları hiç kullanamaz.

Kodlama, IMO'nun bu şekilde daha hızlı ve daha temiz görünmesini sağlıyor. Yalnızca işlevler yapıyorsanız ve devralınan özelliklere ihtiyacınız yoksa işlevleri kullanın.


2

C # 8 ile artık arayüz üyelerinin varsayılan uygulaması aracılığıyla pratik olarak birden fazla mirasınız var:

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
}

4
Evet, ancak yukarıda, yapamayacağınıza dikkat edin new ConsoleLogger().Log(someEception)- işe yaramaz, ILoggervarsayılan arabirim yöntemini kullanmak için nesnenizi açıkça bir . Bu yüzden kullanışlılığı biraz sınırlı.
Dmitri Nesteruk

1

IFirst ve ISecond yöntemlerinin yalnızca IFirst ve ISecond sözleşmesiyle (örneğin örneğiniz gibi) etkileşimde bulunması gerektiği kısıtlamasıyla yaşayabiliyorsanız ... uzatma yöntemleriyle istediğinizi yapabilirsiniz. Pratikte, bu nadiren olur.

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();
}

Dolayısıyla temel fikir, arayüzlerde gerekli uygulamayı tanımlamanızdır ... bu gerekli şeyler, uzantı yöntemlerindeki esnek uygulamayı desteklemelidir. Her zaman bir uzantı yöntemi eklemek yerine "arabirime yöntemler eklemek" gerekir.


1

Evet, arabirimi kullanmak bir güçlüktür, çünkü sınıfa bir yöntem eklediğimizde, arabirime imza eklememiz gerekir. Ayrıca, zaten bir grup yöntemle bir sınıfımız varsa, ancak bunun için Arabirim yoksa ne olur? devralmak istediğimiz tüm sınıflar için el ile Arabirim oluşturmalıyız. Ve en kötüsü, eğer çocuk sınıfı çoklu arayüzden miras alacaksa, çocuk sınıfındaki Arabirimlerdeki tüm yöntemleri uygulamalıyız.

Cephe tasarım desenini takip ederek, erişimciler kullanarak birden fazla sınıftan miras almayı simüle edebiliriz . Sınıfları, devralınması gereken sınıf içinde {get; set;} ile özellikler olarak ilan edin ve tüm ortak özellikler ve yöntemler bu sınıftan ve alt sınıfın yapıcısında üst sınıfları başlatın.

Örneğin:

 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();
         }
     }


 }

Bu yapı sınıfı ile Çocuk, ana babaların bir örneğini miras alan çoklu miras simülasyonu yapan Sınıf Baba ve Anne'nin tüm yöntem ve özelliklerine erişebilir. Tam olarak aynı değil ama pratik.


2
İlk paragrafa katılmıyorum. Yalnızca HER sınıfta istediğiniz yöntemlerin imzalarını arabirime eklersiniz. Ancak herhangi bir sınıfa istediğiniz kadar ek yöntem ekleyebilirsiniz. Ayrıca, arayüzleri ayıklama işini basitleştiren bir sağ tıklama, çıkarma arayüzü vardır. Son olarak, örneğiniz hiçbir şekilde miras değildir (çoklu veya başka türlü), ancak kompozisyonun harika bir örneğidir. Ne yazık ki, yapıcı / özellik enjeksiyonu kullanarak DI / IOC'yi göstermek için arabirimler kullanmış olsaydınız daha fazla liyakat olurdu. Oy vermeyeceğim ama bunun iyi bir cevap olduğunu düşünmüyorum üzgünüm.
Francis Rodgers

1
Bir yıl sonra bu iş parçacığına baktığımda, arayüzde imza eklemeden sınıfta istediğiniz kadar yöntem ekleyebileceğinizi kabul ediyorum, ancak bu, arabirimin eksik olmasını sağlayacaktır. Ayrıca, IDE'mde sağ tıklama-çıkarma arayüzünü bulamadım, belki bir şey eksikti. Ancak, daha büyük endişem, arayüzde bir imza belirttiğinizde, devralma sınıflarının bu imzayı uygulaması gerekir. Bunun çift çalışma olduğunu ve kodların tekrarlanmasına yol açabileceğini düşünüyorum.
Yogi

ÖZÜ ARAYÜZ: Sınıf imza üzerinde sağ tıklayın, ardından arabirim ayıklamak ... VS2015 aynı süreçte dışında sağ tıklamanız gerekir, o zaman seçim Quick Actions and Refactorings...bu bilmelidir olduğunu, bu size çok zaman kazandıracak
Chef_Code

1

Hepimiz bununla arayüz yoluna doğru ilerliyoruz gibi görünüyor, ancak burada açık olan diğer olasılık, OOP'un yapması gereken şeyi yapmak ve miras ağacınızı oluşturmak ... (sınıf tasarımının hepsi bu değil mi?) hakkında?)

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; }
}

Bu yapı yeniden kullanılabilir kod blokları sağlar ve şüphesiz OOP kodu nasıl yazılmalıdır?

Bu özel yaklaşım faturaya tam olarak uymuyorsa, sadece gerekli nesnelere dayalı yeni sınıflar yaratırız ...

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; }
}

0

Çoklu kalıtım genellikle çözdüğünden daha fazla soruna neden olan şeylerden biridir. C ++ 'da, kendinizi asmak için yeterli ip verme desenine uyuyor, ancak Java ve C # size seçeneği vermemenin daha güvenli yolunu seçti. En büyük sorun, mirasın uygulamadığı aynı imzayla bir yöntemi olan birden fazla sınıfı devralırsanız ne yapmanız gerektiğidir. Hangi sınıfın yöntemini seçmeli? Yoksa bu derlenmemeli mi? Çoğunlukla birden fazla mirasa dayanmayan çoğu şeyi uygulamanın başka bir yolu vardır.


8
Lütfen MI'yi C ++ ile yargılamayın, bu OOP'yi PHP veya otomobilleri Pintos tarafından değerlendirmek gibidir. Bu sorun kolayca çözülebilir: Eiffel'de, bir sınıftan miras aldığınızda, hangi yöntemleri devralmak istediğinizi belirtmeniz gerekir ve bunları yeniden adlandırabilirsiniz. Orada hiçbir belirsizlik ve sürpriz de yok.
Jörg W Mittag

2
mP: hayır, Eiffel gerçek çoklu uygulama mirası sağlıyor. Yeniden adlandırma, miras zincirini kaybetmek veya sınıfların dökülebilirliğini kaybetmek anlamına gelmez.
Abel

0

X, Y'den miras alırsa, bunun iki dikey etkisi vardır:

  1. Y, X için varsayılan işlevsellik sağlar, bu nedenle X kodunun yalnızca Y'den farklı şeyler içermesi gerekir.
  2. Hemen hemen her yerde bir Y bekleniyordu, bunun yerine bir X kullanılabilir.

Kalıtım her iki özelliği de sağlasa da, ikisinin de diğeri olmadan kullanılabileceği durumları hayal etmek zor değildir. Bildiğim hiçbir .net dili, ikincisi olmadan ilkini uygulamanın doğrudan bir yoluna sahip değildir, ancak hiçbir zaman doğrudan kullanılmayan bir temel sınıfı tanımlayarak ve hiçbir şey eklemeden doğrudan ondan miras alan bir veya daha fazla sınıfa sahip olarak bu işlevselliği elde edebilir. yeni (bu tür sınıflar tüm kodlarını paylaşabilirler, ancak birbirlerinin yerine geçemezler). Bununla birlikte, CLR uyumlu herhangi bir dil, ilk (üye yeniden kullanımı) olmadan arabirimlerin ikinci özelliğini (ikame edilebilirlik) sağlayan arabirimlerin kullanımına izin verecektir.


0

biliyorum izin bile izin verilmez ve böylece, bazen u aslında olanlar için çok gerekli:

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

benim durumumda olduğu gibi bu sınıf b yapmak istedim: Form (yep windows.forms) sınıf c: b {}

çünkü fonksiyonun yarısı aynıdır ve arayüz ile u hepsini yeniden yazmalıdır


1
Sizin örnek çoklu miras, yani ne sorun vardır tasvir etmez sen çözmeye çalışırken? Gerçek bir çoklu miras örneği gösterecektir class a : b, c(gerekli sözleşme deliklerinin uygulanması). Belki de örnekleriniz biraz daha basitleştirilmiştir?
M.Babcock

0

Birden fazla miras (MI) sorunu zaman zaman ortaya çıktığı için, kompozisyon deseniyle ilgili bazı sorunları ele alan bir yaklaşım eklemek istiyorum.

Ben üzerine inşa IFirst, ISecond, First, Second, FirstAndSecondsöz konusu sunuldu olarak, yaklaşımı. Örnek kodunu IFirst, arayüz sayısı / MI temel sınıflarının sayısına bakılmaksızın aynı kaldığından düşürürüm.

Diyelim ki MI ile Firstve Secondher ikisi de aynı temel sınıftan BaseClasstüreyecek,BaseClass

Bu BaseClass, Firstve Seconduygulamasında bir kapsayıcı başvurusu ekleyerek ifade edilebilir :

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

Korunan arabirim öğelerine BaseClassreferans verildiğinde veya MI'da soyut sınıflar olduğunda Firstve Secondne zaman olacağı daha karmaşık hale gelir ve alt sınıflarının bazı soyut bölümleri uygulamasını gerektirir.

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 #, iç içe sınıfların, içerdiği sınıfların korumalı / özel öğelerine erişmesine izin verir, böylece bu, Firstuygulamadan soyut bitleri bağlamak için kullanılabilir .

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"); }
}

Oldukça biraz kazan plakası var, ancak FirstMethod ve SecondMethod'un gerçek uygulaması yeterince karmaşıksa ve erişilen özel / korumalı yöntemlerin miktarı orta düzeydeyse, bu model çoklu kalıtım eksikliğinin üstesinden gelmeye yardımcı olabilir.


0

Bu Lawrence Wenham'ın cevabı boyunca, ancak kullanım durumunuza bağlı olarak, bir gelişme olabilir veya olmayabilir - ayarlayıcılara ihtiyacınız yoktur.

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; }
}

Artık bir kişinin nasıl alınacağını bilen herhangi bir nesne IGetPerson'u uygulayabilir ve otomatik olarak GetAgeViaPerson () ve GetNameViaPerson () yöntemlerine sahip olacaktır. Bu noktadan itibaren, temelde tüm Kişi kodu, her ikisine de girmesi gereken yeni ivarlar dışında IPerson'a değil, IGetPerson'a girer. Ve böyle bir kodu kullanırken, IGetPerson nesnenizin kendisinin aslında bir IPerson olup olmadığı konusunda endişelenmenize gerek yoktur.


0

Bu artık mümkün partialsınıflar arasındadır, her biri kendi başına bir sınıfı devralabilir ve son nesneyi tüm temel sınıfları devralabilir. Burada daha fazla bilgi edinebilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.