C # में कई वंशानुक्रम


212

चूंकि मल्टीपल इनहेरिटेंस खराब है (यह स्रोत को अधिक जटिल बनाता है) सी # सीधे ऐसा पैटर्न प्रदान नहीं करता है। लेकिन कभी-कभी यह क्षमता रखने में मददगार होगा।

उदाहरण के लिए, मैं इंटरफेस और तीन वर्गों का उपयोग करते हुए लापता कई विरासत पैटर्न को लागू करने में सक्षम हूं:

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

हर बार जब मैं क्लास फ़र्स्टएंडसेकंड को भी बदलने की ज़रूरत होती है, तो एक इंटरफेस में से एक को जोड़ देता हूं ।

क्या कई मौजूदा वर्गों को एक नई कक्षा में इंजेक्ट करने का एक तरीका है जैसे कि C ++ में संभव है?

हो सकता है कि किसी प्रकार की कोड पीढ़ी का उपयोग करके कोई समाधान हो?

या यह इस तरह दिख सकता है (काल्पनिक सी # वाक्य रचना):

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

जब मैं इंटरफ़ेस में से एक को संशोधित करता हूं तो क्लास फर्स्टएंडसेकंड को अपडेट करने की आवश्यकता नहीं होगी।


संपादित करें

शायद एक व्यावहारिक उदाहरण पर विचार करना बेहतर होगा:

आपके पास एक मौजूदा वर्ग है (उदाहरण के लिए ITextTcpClient पर आधारित एक पाठ आधारित टीसीपी क्लाइंट) जो आप पहले से ही अपनी परियोजना के अंदर विभिन्न स्थानों पर उपयोग करते हैं। अब आप विंडोज़ फॉर्म डेवलपर्स के लिए आसान पहुँच के लिए अपनी कक्षा का एक घटक बनाने की आवश्यकता महसूस करते हैं।

जहाँ तक मैं जानता हूँ कि वर्तमान में आपके पास ऐसा करने के दो तरीके हैं:

  1. एक नया वर्ग लिखें जो कि घटकों से विरासत में मिला है और TextTcpClient वर्ग के इंटरफ़ेस को लागू करता है जो कि कक्षा के उदाहरण के साथ फर्स्टएंडसेकंड के साथ दिखाया गया है।

  2. एक नया वर्ग लिखें जो TextTcpClient से विरासत में मिले और किसी तरह IComponent को लागू करता है (वास्तव में अभी तक यह कोशिश नहीं की गई है)।

दोनों ही मामलों में आपको प्रति विधि के अनुसार काम करने की आवश्यकता है न कि प्रति कक्षा। चूंकि आप जानते हैं कि हमें TextTcpClient और घटक के सभी तरीकों की आवश्यकता होगी, इसलिए यह उन दो को एक कक्षा में संयोजित करने का सबसे आसान समाधान होगा।

संघर्ष से बचने के लिए यह कोड पीढ़ी द्वारा किया जा सकता है जहां परिणाम बाद में बदला जा सकता है लेकिन हाथ से टाइप करना गधे में एक शुद्ध दर्द है।


इस हद तक कि यह केवल भेस में कई विरासत नहीं है, यह कम जटिल कैसे है?
harpo

3.5 में नई विस्तार विधियों के बारे में सोचना और काम करने का तरीका (स्टैटिक मेंबर कॉल जनरेशन), यह अगले .NET लैंग्वेज इवोल्यूशन में से एक हो सकता है।
लैरी

कभी-कभी मुझे आश्चर्य होता है कि लोग ऐसा क्यों नहीं करते ... क्लास ए: क्लास बी: क्लास सी?
चिब्यूज ओपटा


9
प्रचार को मूर्ख मत बनने दो। आपका बहुत ही उदाहरण दिखाता है कि कई उत्तराधिकार उपयोगी हैं और इसकी कमी के लिए इंटरफेस सिर्फ एक समाधान है
केमल एर्दोगन

जवाबों:


125

चूंकि मल्टीपल इनहेरिटेंस खराब है (यह स्रोत को अधिक जटिल बनाता है) सी # सीधे ऐसा पैटर्न प्रदान नहीं करता है। लेकिन कभी-कभी यह क्षमता रखने में मददगार होगा।

C # और .net CLR ने MI को लागू नहीं किया है क्योंकि उन्होंने यह निष्कर्ष नहीं निकाला है कि यह C #, VB.net और अन्य भाषाओं के बीच अभी तक कैसे संचालित होगा, इसलिए नहीं कि "यह स्रोत को और अधिक जटिल बना देगा"

एमआई एक उपयोगी अवधारणा है, गैर-उत्तर वाले प्रश्न इस प्रकार हैं: - "जब आप विभिन्न सुपरक्लासेस में कई सामान्य आधार कक्षाएं रखते हैं तो आप क्या करते हैं?

पर्ल एकमात्र ऐसी भाषा है जिसे मैंने कभी भी काम किया है जहां एमआई काम करता है और अच्छी तरह से काम करता है। । नेट इसे एक दिन अच्छी तरह से पेश कर सकता है लेकिन अभी तक नहीं, सीएलआर पहले से ही एमआई का समर्थन करता है, लेकिन जैसा कि मैंने कहा है, इसके अलावा अभी तक इसके लिए कोई भाषा निर्माण नहीं हैं।

तब तक आप प्रॉक्सी ऑब्जेक्ट्स और मल्टीपल इंटरफेस के बजाय अटक जाते हैं :(


39
सीएलआर कई कार्यान्वयन वंशानुक्रम का समर्थन नहीं करता है, केवल कई इंटरफ़ेस वंशानुक्रम (जो C # में भी समर्थित है)।
जोर्डो

4
@ जोर्डो: पूर्णता के लिए: कंपाइलरों के लिए CLR में उनके प्रकारों के लिए MI बनाना संभव है। यह है कि यह caveats है, यह उदाहरण के लिए सीएलएस अनुरूप नहीं है। अधिक जानकारी के लिए इसे देखें (2004) लेख blogs.msdn.com/b/csharpfaq/archive/2004/03/07/…
DVDvorle

2
@ मृगप्पी: बहुत दिलचस्प लेख। मैंने वास्तव में C # के लिए Trait संरचना के कुछ तरीके की जांच की है, एक नज़र डालें।
जोर्डो

10
@ मनदीप जंजुआ ने ऐसी किसी भी बात का दावा नहीं किया, मैंने कहा 'अच्छी तरह से पेश कर सकता है' यह तथ्य यह है कि ईसीएमए मानक सीएलआर कई विरासत के लिए आईएल मशीनरी प्रदान करता है, बस कुछ भी नहीं है कि इसका पूरी तरह से उपयोग नहीं करता है।
इयानॉर्टन

4
FYI करें कई विरासत बुरा नहीं है, और कोड को जटिल नहीं बनाता है। बस सोचा था कि मैं इसका उल्लेख करूंगा।
दिमित्री नेस्टरुक

214

मल्टीपल इनहेरिटेंस अनुकरण करने के बजाय केवल रचना का उपयोग करने पर विचार करें । आप यह परिभाषित करने के लिए इंटरफेस का उपयोग कर सकते हैं कि कौन सी कक्षाएं संरचना बनाती हैं, उदाहरण के लिए: ISteerableप्रकार की एक संपत्ति का अर्थ है SteeringWheel, प्रकार की एक संपत्ति का IBrakableअर्थ है BrakePedalआदि।

एक बार जब आप ऐसा कर लेते हैं, तो आप उन निहित गुणों पर कॉलिंग विधियों को सरल बनाने के लिए C # 3.0 में जोड़े गए एक्सटेंशन मेथड्स फीचर का उपयोग कर सकते हैं , जैसे:

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
हालांकि यह बात है - इस तरह का एक विचार रचना को आसान करेगा।
जॉन स्कीट

9
हां, लेकिन ऐसे मामले हैं जहां आपको मुख्य वस्तु के हिस्से के रूप में तरीकों की आवश्यकता है
डेविड पियरे

9
दुर्भाग्य से सदस्य चर डेटा विस्तार विधियों में सुलभ नहीं है, इसलिए आपको उन्हें आंतरिक या (कुरूप) सार्वजनिक रूप से उजागर करना होगा, हालांकि मुझे लगता है कि अनुबंध द्वारा रचना कई विरासत को हल करने का सबसे अच्छा तरीका है।
cfeduke

4
बहुत बढ़िया जवाब! संक्षिप्त, समझने में आसान, बहुत उपयोगी चित्रण। धन्यवाद!
ए जे।

3
हम जाँच करना चाहते हैं myCarकि क्या कॉलिंग से पहले स्टीयरिंग बचेगा Stop। यदि अत्यधिक गति में हो तो इसे Stopलागू किया जा सकता myCarहै। : D
देवराज गढ़वी

16

मैंने एक C # पोस्ट-कंपाइलर बनाया जो इस तरह की चीज़ों को सक्षम बनाता है:

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

आप पोस्ट-कंपाइलर को Visual Studio पोस्ट-बिल्ड-ईवेंट के रूप में चला सकते हैं:

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

उसी सभा में आप इसे इस तरह उपयोग करते हैं:

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

एक अन्य विधानसभा में आप इसे इस तरह उपयोग करते हैं:

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

6

आपके पास एक सार आधार वर्ग हो सकता है जो IFirst और ISecond दोनों को लागू करता है, और फिर बस उस आधार से विरासत में मिलता है।


यह शायद सबसे अच्छा समाधान है, लेकिन जरूरी नहीं कि सबसे अच्छा विचार: p
leppie

1
जब आप इंटरफेसेस के लिए तरीके जोड़ते हैं तो क्या आपको अभी भी अमूर्त वर्ग को संपादित नहीं करना पड़ेगा?
ऋक्

Rik: बस तुम कितने आलसी हो, जब तुम्हें केवल एक बार ऐसा करना होगा?
लीपी

3
@leppie - "हर बार जब मैं क्लास फ़र्स्टएंडसॉन्ड के साथ-साथ क्लास को बदलने के लिए किसी एक इंटरफेस को जोड़ता हूं।" मूल प्रश्न का यह हिस्सा इस समाधान द्वारा संबोधित नहीं है, क्या यह है?
रिक ऑक्ट

2
आपको अमूर्त वर्ग को संपादित करना होगा, लेकिन आपको उस पर निर्भर किसी अन्य वर्ग को संपादित करने की आवश्यकता नहीं है। हिरन वहाँ रुक जाता है, बजाय कक्षाओं के पूरे संग्रह को जारी रखने के।
जोएल कोइहॉर्न 14

3

एमआई बुरा नहीं है, हर किसी ने (गंभीरता से) इसका इस्तेमाल किया है और यह कोड को जटिल नहीं करता है! कम से कम अन्य निर्माणों की तुलना में अब कोड को जटिल नहीं किया जा सकता है। एमआई कोड तस्वीर में है या नहीं, इसके बावजूद बुरा कोड खराब कोड है।

वैसे भी, मुझे एक से अधिक घेरने के लिए एक अच्छा सा समाधान मिला है जिसे मैं साझा करना चाहता था, यह है; http://ra-ajax.org/lsp-liskov-substoration-principle-to-be-or-not-to-be.blog या आप मेरे sig में लिंक का अनुसरण कर सकते हैं ... :)


क्या कई विरासत होना संभव है और अपस्टेक और डाउनकास्ट होने के दौरान पहचान-संरक्षण हो सकता है? मैं कई उत्तराधिकारियों की समस्याओं के लिए जो समाधान जानता हूं वह उन जातियों के इर्द-गिर्द घूमता है जो पहचान-संरक्षण नहीं कर रही हैं (यदि myFooवह प्रकार है Foo, जो विरासत में मिली है Mooऔर Gooजो दोनों से विरासत में मिली है Boo, तब (Boo)(Moo)myFooऔर उसके (Boo)(Goo)myFooबराबर नहीं होगी)। क्या आप किसी भी पहचान-संरक्षण दृष्टिकोण के बारे में जानते हैं?
सुपरकैट

1
आप लिंक का अनुसरण करने के लिए web.archive.org या इसी तरह का उपयोग कर सकते हैं, लेकिन यह मूल प्रश्न में दिए गए समाधान के बारे में अधिक विस्तृत चर्चा करता है।
माइकबटन

2

अपने स्वयं के कार्यान्वयन में मैंने पाया कि एमआई के लिए कक्षाओं / इंटरफेस का उपयोग करना, हालांकि "अच्छा रूप", जटिलता के कारण बड़े पैमाने पर हो गया क्योंकि आपको केवल कुछ आवश्यक फ़ंक्शन कॉल के लिए सभी विरासत को स्थापित करने की आवश्यकता है, और मेरे मामले में, वस्तुतः दर्जनों बार अनावश्यक रूप से किए जाने की आवश्यकता है।

इसके बजाय केवल OOP रिप्लेसमेंट के रूप में अलग-अलग मॉड्यूलर किस्मों में स्थिर "फ़ंक्शंस जो कॉल फ़ंक्शंस को कॉल फ़ंक्शन करता है" को करना आसान था। जिस समाधान पर मैं काम कर रहा था वह एक आरपीजी के लिए "वर्तनी प्रणाली" था जहां प्रभाव को भारी करने की आवश्यकता होती है को फिर से लिखने के कोड के बिना मंत्रों की एक चरम विविधता देने के - मिश्रण और मैच फ़ंक्शन को कॉल करने की आवश्यकता होती है, उदाहरण के लिए ऐसा लगता है।

अधिकांश फ़ंक्शंस अब स्थिर हो सकते हैं क्योंकि मुझे वर्तनी तर्क के लिए एक उदाहरण की आवश्यकता नहीं है, जबकि क्लास इनहेरिटेंस भी स्थिर रहते हुए वर्चुअल या सार कीवर्ड का उपयोग नहीं कर सकता है। इंटरफेस उनका उपयोग बिल्कुल नहीं कर सकते हैं।

कोडिंग तेजी से और इस तरह से साफ करता है IMO। यदि आप केवल कार्य कर रहे हैं, और विरासत में मिली संपत्तियों की आवश्यकता नहीं है , तो कार्यों का उपयोग करें।


2

C # 8 के साथ अब आपके पास इंटरफ़ेस सदस्यों के डिफ़ॉल्ट कार्यान्वयन के माध्यम से व्यावहारिक रूप से कई विरासत हैं:

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
हां, लेकिन ध्यान दें कि, उपरोक्त में, आप ऐसा करने में सक्षम नहीं होंगे new ConsoleLogger().Log(someEception)- यह बस काम नहीं करेगा, आपको ILoggerडिफ़ॉल्ट इंटरफ़ेस पद्धति का उपयोग करने के लिए अपनी वस्तु को स्पष्ट रूप से डालना होगा । तो इसकी उपयोगिता कुछ हद तक सीमित है।
दिमित्री नेस्टरुक

1

यदि आप इस प्रतिबंध के साथ रह सकते हैं कि IFirst और ISecond के तरीकों को केवल IFirst और ISecond के अनुबंध के साथ बातचीत करनी चाहिए (जैसे आपके उदाहरण में) ... आप वही कर सकते हैं जो आप विस्तार के तरीकों से पूछते हैं। व्यवहार में, यह शायद ही कभी होता है।

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

इसलिए मूल विचार यह है कि आप इंटरफेस में आवश्यक कार्यान्वयन को परिभाषित करते हैं ... इस आवश्यक सामान को विस्तार विधियों में लचीले कार्यान्वयन का समर्थन करना चाहिए। कभी भी आपको "इंटरफ़ेस में विधियों को जोड़ने" की आवश्यकता होती है बजाय इसके कि आप एक एक्सटेंशन विधि जोड़ें।


1

हाँ, इंटरफ़ेस का उपयोग करना एक परेशानी है क्योंकि कभी भी हम क्लास में एक विधि जोड़ते हैं जिसे हमें इंटरफ़ेस में हस्ताक्षर जोड़ना पड़ता है। इसके अलावा, क्या होगा यदि हमारे पास पहले से ही तरीकों की एक गुच्छा के साथ एक वर्ग है लेकिन इसके लिए कोई इंटरफ़ेस नहीं है? हमें उन सभी वर्गों के लिए मैन्युअल रूप से इंटरफ़ेस बनाना होगा जिन्हें हम विरासत में प्राप्त करना चाहते हैं। और सबसे बुरी बात यह है, हमें बाल वर्ग में इंटरफेसेस में सभी तरीकों को लागू करना होगा यदि बाल वर्ग को कई इंटरफ़ेस से विरासत में प्राप्त करना है।

फसाड डिज़ाइन पैटर्न का अनुसरण करके हम प्रयोग कर कई वर्गों से इनहेरिट अनुकरण कर सकते हैं accessors । वर्ग को {get; set;} के साथ गुणों के रूप में घोषित करें। उस वर्ग के अंदर जिसे सभी सार्वजनिक गुणों और विधियों की आवश्यकता होती है, उस वर्ग से होते हैं, और बच्चे के निर्माण में माता-पिता की कक्षाओं को तुरंत बदल देते हैं।

उदाहरण के लिए:

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


 }

इस संरचना वर्ग के साथ बालकों के पास अभिभावक वर्गों के एक उदाहरण को प्राप्त करते हुए, क्लास फ़ादर और मदर के सभी तरीकों और गुणों तक पहुँच होगी, जो कई विरासतों का अनुकरण करते हैं। काफी समान नहीं है, लेकिन यह व्यावहारिक है।


2
मैं पहले पैराग्राफ से असहमत हूं। आप केवल उन तरीकों के हस्ताक्षर जोड़ते हैं जो आप इंटरफ़ेस में चाहते हैं। लेकिन आप किसी भी वर्ग के लिए जितने चाहें उतने अतिरिक्त तरीके जोड़ सकते हैं। इसके अलावा, एक राइट क्लिक, एक्सट्रैक्ट इंटरफेस है जो इंटरफेस को निकालने का काम सरल बनाता है। अंत में, आपका उदाहरण किसी भी तरह से वंशानुक्रम (एकाधिक या अन्यथा) नहीं है, हालांकि यह रचना का एक बड़ा उदाहरण है। काश, इसमें अधिक योग्यता होती कि आपने इंटरफेस का उपयोग डिस्ट्रक्टर / प्रॉपर्टी इंजेक्शन का उपयोग करके DI / IOC को प्रदर्शित करने के लिए भी किया होता। हालांकि मैं वोट नहीं करता, मुझे नहीं लगता कि इसका अच्छा जवाब माफ़ करना चाहिए।
फ्रांसिस रोडर्स

1
एक साल बाद इस थ्रेड को देखते हुए, मैं मानता हूं कि आप इंटरफ़ेस में हस्ताक्षर को जोड़े बिना वर्ग में जितनी विधियां चाहते हैं, जोड़ सकते हैं, हालांकि, यह इंटरफ़ेस को अपूर्ण बना देगा। इसके अलावा, मैं अपनी IDE में राइट-क्लिक - एक्सट्रैक्ट इंटरफ़ेस नहीं ढूंढ पा रहा था, शायद मुझे कुछ याद आ रहा था। लेकिन, मेरी बड़ी चिंता यह है कि जब आप इंटरफ़ेस में एक हस्ताक्षर निर्दिष्ट करते हैं, तो विरासत वाले वर्गों को उस हस्ताक्षर को लागू करना चाहिए। मुझे लगता है कि यह दोहरा काम है और इससे कोडों का दोहराव हो सकता है।
योगी

EXTRACT INTERFACE: क्लास सिग्नेचर पर राइट क्लिक करें, फिर इंटरफेस निकालें ... VS2015 में भी यही प्रक्रिया है, इसके अलावा आपको राइट क्लिक करना होगा, फिर इसे चुनें Quick Actions and Refactorings..., यह जरूर जान लें, इससे आपका काफी समय
बच

1

हम सभी को इसके साथ इंटरफेस पथ नीचे दिखाई दे रहा है, लेकिन स्पष्ट रूप से अन्य संभावना, यहाँ, ओओपी को क्या करना है, और अपने वंशानुक्रम को बनाने के लिए क्या करना है ... (क्या यह वर्ग डिजाइन सब नहीं है) के बारे में?)

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

यह संरचना कोड के पुन: प्रयोज्य ब्लॉक प्रदान करती है और, निश्चित रूप से, ओओपी कोड कैसे लिखा जाना चाहिए?

यदि यह विशेष दृष्टिकोण बिल फिट नहीं करता है तो हम केवल आवश्यक वस्तुओं के आधार पर नई कक्षाएं बनाते हैं ...

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

मल्टीपल इनहेरिटेंस उन चीजों में से एक है जो आम तौर पर हल करने की तुलना में अधिक समस्याओं का कारण बनती है। C ++ में यह आपको अपने आप को लटकाने के लिए पर्याप्त रस्सी देने के पैटर्न को फिट करता है, लेकिन जावा और C # ने आपको विकल्प नहीं देने के सुरक्षित मार्ग पर जाने के लिए चुना है। सबसे बड़ी समस्या यह है कि यदि आप एक ही हस्ताक्षर के साथ एक विधि है कि विरासत को लागू नहीं करता है के साथ एक विधि है, तो कई वर्गों को क्या करना है। किस वर्ग का तरीका चुनना चाहिए? या कि संकलन नहीं करना चाहिए? आमतौर पर अधिकांश चीजों को लागू करने का एक और तरीका है जो कई विरासतों पर भरोसा नहीं करता है।


8
कृपया C ++ द्वारा MI को जज न करें, यह PHP या ऑटोमोबाइल द्वारा POPos द्वारा OOP को जज करने जैसा है। यह समस्या आसानी से हल करने योग्य है: एफिल में, जब आप एक वर्ग से विरासत में मिलते हैं, तो आपको यह भी निर्दिष्ट करना होगा कि आपको कौन सी विधियाँ विरासत में मिलनी हैं और आप उनका नाम बदल सकते हैं। वहां कोई अस्पष्टता और कोई अतिशयता नहीं है।
जोर्ग डब्ल्यू मित्तग

2
@ एमपी: नहीं, एफिल सच कई कार्यान्वयन विरासत प्रदान करता है। नाम बदलने का मतलब वंशानुक्रम श्रृंखला को खोना नहीं है, और न ही यह कक्षाओं की विश्वसनीयता को ढीला करेगा।
हाबिल

0

यदि X, Y से विरासत में मिला है, तो इसके कुछ हद तक रूढ़िवादी प्रभाव हैं:

  1. Y, X के लिए डिफ़ॉल्ट कार्यक्षमता प्रदान करेगा, इसलिए X के लिए कोड में केवल सामान शामिल करना होगा जो Y से अलग है।
  2. लगभग कहीं भी एक वाई की उम्मीद की जाएगी, इसके बजाय एक एक्स का उपयोग किया जा सकता है।

यद्यपि इनहेरिटेंस दोनों विशेषताओं के लिए प्रदान करता है, यह उन परिस्थितियों की कल्पना करना मुश्किल नहीं है जहां या तो दूसरे के बिना उपयोग हो सकता है। कोई भी .net भाषा नहीं जिसे मैं जानता हूं कि दूसरे के बिना पहले को लागू करने का एक सीधा तरीका है, हालांकि एक आधार वर्ग को परिभाषित करके ऐसी कार्यक्षमता प्राप्त कर सकता है, जिसका कभी भी सीधे उपयोग नहीं किया जाता है, और एक या एक से अधिक वर्ग होने के बिना कुछ भी जोड़कर सीधे विरासत में मिलता है। नया (ऐसी कक्षाएं अपने सभी कोड साझा कर सकती हैं, लेकिन एक दूसरे के लिए उपयुक्त नहीं होंगी)। किसी भी सीएलआर-अनुरूप भाषा, हालांकि, पहले (सदस्य पुन: उपयोग) के बिना इंटरफेस (प्रतिस्थापन) की दूसरी विशेषता प्रदान करने वाले इंटरफेस के उपयोग की अनुमति देगा।


0

मुझे पता है कि मुझे पता है भले ही इसकी अनुमति नहीं है और इतने पर, कभी-कभी आपको वास्तविक रूप से इसकी आवश्यकता होती है:

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

जैसे मेरे मामले में मैं इस वर्ग को करना चाहता था b: फ़ॉर्म (windows.forms yep) वर्ग c: b {}

फ़ंक्शन का आधा कारण समान था और इंटरफ़ेस यू के साथ उन सभी को फिर से लिखना चाहिए


1
आपका उदाहरण एकाधिक उत्तराधिकार का चित्रण नहीं करता है, इसलिए आप किस समस्या को हल करने का प्रयास कर रहे हैं ? एक वास्तविक बहु विरासत उदाहरण दिखाएगा class a : b, c(किसी भी आवश्यक अनुबंध छेद को लागू करना)। शायद आपके उदाहरण सरल हो गए हैं?
एम। बेबॉक

0

चूंकि कई उत्तराधिकार (MI) के प्रश्न समय-समय पर आते रहते हैं, इसलिए मैं एक दृष्टिकोण जोड़ना चाहूंगा जो रचना पैटर्न के साथ कुछ समस्याओं को हल करता है।

मैं का निर्माण IFirst, ISecond, First, Second, FirstAndSecond, दृष्टिकोण के रूप में यह प्रश्न में पेश किया गया। मैं नमूना कोड को कम कर देता हूं IFirst, क्योंकि पैटर्न इंटरफेस / एमआई बेस कक्षाओं की संख्या की परवाह किए बिना समान रहता है।

चलो मान लेते हैं, कि एमआई के साथ Firstऔर Secondदोनों एक ही आधार वर्ग से प्राप्त होंगे BaseClass, केवल सार्वजनिक इंटरफ़ेस तत्वों सेBaseClass

यह व्यक्त किया जा सकता, करने के लिए एक कंटेनर संदर्भ जोड़कर BaseClassमें Firstऔर Secondकार्यान्वयन:

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

हालात बन अधिक जटिल जब से संरक्षित इंटरफ़ेस तत्वों BaseClassसंदर्भित कर रहे हैं या जब Firstऔर Secondएमआई में सार कक्षाएं होगा, उनके उपवर्गों की आवश्यकता होती है कुछ सार भागों को लागू करने।

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 # नेस्टेड क्लासेस को उनके युक्त वर्गों के संरक्षित / निजी तत्वों को एक्सेस करने की अनुमति देता है, इसलिए इसका उपयोग Firstकार्यान्वयन से अमूर्त बिट्स को जोड़ने के लिए किया जा सकता है ।

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

इसमें कुछ बॉयलरप्लेट शामिल हैं, लेकिन अगर फर्स्टमेथोड और सेकंडमेथोड का वास्तविक कार्यान्वयन पर्याप्त रूप से जटिल है और एक्सेस की गई निजी / संरक्षित विधियों की मात्रा मध्यम है, तो यह पैटर्न कई वंशानुक्रमों की कमी को दूर करने में मदद कर सकता है।


0

यह लॉरेंस वेन्हम के उत्तर की तर्ज पर है, लेकिन आपके उपयोग के मामले के आधार पर, यह एक सुधार हो सकता है या नहीं भी हो सकता है - आपको बसने की आवश्यकता नहीं है।

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

अब कोई भी वस्तु जो जानता है कि किसी व्यक्ति को आईजीपेटरसन कैसे लागू किया जा सकता है, और इसमें स्वचालित रूप से गेटएविवियापर्सन () और गेटनेमियापर्सन () विधियां होंगी। इस बिंदु से, मूल रूप से सभी व्यक्ति कोड IGetPerson में जाते हैं, न कि IPerson में, नए ivars के अलावा, जिन्हें दोनों में जाना है। और इस तरह के कोड का उपयोग करने के लिए, आपको इस बारे में चिंतित होने की आवश्यकता नहीं है कि आपका IGetPerson ऑब्जेक्ट वास्तव में एक IPerson है या नहीं।


0

यह अब संभव है गर्त partialकक्षाएं, उनमें से प्रत्येक अपने आप ही एक वर्ग को विरासत में दे सकती है, जिससे अंतिम वस्तु सभी आधार वर्गों को विरासत में मिलती है। आप इसके बारे में यहां और जान सकते हैं ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.