2008-09-05 13 views
8

मुझे कुछ विरासत समस्याएं हैं क्योंकि मुझे अंतर-संबंधित अमूर्त वर्गों का एक समूह मिला है जिसे क्लाइंट कार्यान्वयन के लिए सभी को एक साथ ओवरराइड करने की आवश्यकता है। आदर्श रूप में मैं निम्नलिखित की तरह कुछ करना चाहते हैं:विरासत क्यों काम नहीं करती है जिस तरह से मुझे लगता है कि इसे काम करना चाहिए?

abstract class Animal 
{ 
    public Leg GetLeg() {...} 
} 

abstract class Leg { } 

class Dog : Animal 
{ 
    public override DogLeg Leg() {...} 
} 

class DogLeg : Leg { } 

यह किसी कुत्ते वर्ग का उपयोग कर स्वचालित रूप से DogLegs और पशु वर्ग का उपयोग कर पैर पाने के लिए किसी को भी प्राप्त करने की अनुमति होगी। समस्या यह है कि ओवरराइड फ़ंक्शन को बेस क्लास के समान प्रकार होना चाहिए, इसलिए यह संकलित नहीं होगा। मुझे नहीं लगता कि यह क्यों नहीं होना चाहिए, क्योंकि डॉगलेग पैर के लिए पूरी तरह से जाली योग्य है। मुझे पता है कि इसके चारों ओर बहुत सारे तरीके हैं, लेकिन मैं अधिक उत्सुक हूं कि यह सी # में क्यों संभव नहीं है।

EDIT: मैंने इसे कुछ हद तक संशोधित किया, क्योंकि मैं वास्तव में अपने कोड में कार्यों के बजाय गुणों का उपयोग कर रहा हूं।

संपादित: मैं इसे वापस कार्यों के लिए, बदल क्योंकि उत्तर केवल (एक संपत्ति के सेट समारोह नहीं काम करना चाहिए का मूल्य पैरामीटर पर सहप्रसरण) है कि स्थिति के लिए लागू होता है। उतार-चढ़ाव के लिए खेद है! मुझे एहसास है कि यह बहुत सारे जवाब अप्रासंगिक लगता है।

उत्तर

15

संक्षिप्त उत्तर यह है कि GetLeg अपने रिटर्न प्रकार में परिवर्तनीय है। लंबा उत्तर यहां पाया जा सकता है: Covariance and contravariance

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

+0

भिन्नता पर आपका बयान समझ में नहीं आता है। उपप्रकार में: कोविरिएंस = संबंधित प्रकार उपप्रकार और Contravariance = संबंधित प्रकार supertyped हो रही है। यह प्रश्न एक covariant (उप प्रकार का उपप्रकार में उपयोग किया जाता है) रिटर्न प्रकार का प्रतिनिधित्व करता है, जब सी # केवल एक invariant (समान) वापसी प्रकार की अनुमति देता है। –

+0

जिन लेखों से आप जुड़े हैं वे जेनेरिक प्रकार भिन्नता के बारे में हैं। प्रश्न वापसी प्रकार covariance के बारे में है। मैं उन लेखों में स्पष्ट रूप से बताता हूं कि मैं वापसी प्रकार के बारे में बात नहीं कर रहा हूं। –

+0

एरिक लिपर्ट: लेकिन उन चीजें आंतरिक रूप से जुड़े हुए हैं। यहां देखें: http://apocalisp.wordpress.com/2009/08/27/hostility-toward-subtyping/ – Apocalisp

2

GetLeg() को ओवरराइड होने के लिए पैर वापस करना होगा। हालांकि, आपका कुत्ता वर्ग अभी भी डॉगलेग ऑब्जेक्ट्स लौटा सकता है क्योंकि वे लेग के बाल वर्ग हैं। ग्राहक तब doglegs के रूप में उन्हें कास्ट और संचालित कर सकते हैं।

public class ClientObj{ 
    public void doStuff(){ 
    Animal a=getAnimal(); 
    if(a is Dog){ 
     DogLeg dl = (DogLeg)a.GetLeg(); 
    } 
    } 
} 
6

कुत्ते को लौटने के लिए एक कुत्ते को वापस नहीं करना चाहिए। वास्तविक वर्ग एक डॉगलेग हो सकता है, लेकिन बिंदु decouple है तो कुत्ते के उपयोगकर्ता को DogLegs के बारे में पता नहीं है, उन्हें केवल पैर के बारे में जानने की जरूरत है।

बदलें:

class Dog : Animal 
{ 
    public override DogLeg GetLeg() {...} 
} 

रहे हैं:

class Dog : Animal 
{ 
    public override Leg GetLeg() {...} 
} 

ऐसा नहीं करते हैं:

if(a instanceof Dog){ 
     DogLeg dl = (DogLeg)a.GetLeg(); 

यह सार प्रकार के कार्यक्रमों की करने के उद्देश्य को हरा दिया।

डॉगलेग को छिपाने का कारण यह है कि अमूर्त वर्ग में GetLeg फ़ंक्शन एक सार पैर देता है। यदि आप GetLeg को ओवरराइड कर रहे हैं तो आपको एक पैर वापस करना होगा। एक अमूर्त वर्ग में एक विधि होने का मुद्दा है। उस विधि को अपने बच्चे के लिए प्रचारित करने के लिए। यदि आप कुत्ते के उपयोगकर्ताओं को डॉगलेग्स के बारे में जानना चाहते हैं तो GetDogLeg नामक एक विधि बनाएं और एक डॉगलेग लौटाएं।

यदि आप प्रश्न पूछने वाले के रूप में ऐसा करना चाहते हैं, तो पशु के प्रत्येक उपयोगकर्ता को सभी जानवरों के बारे में जानना होगा।

+0

अप्रासंगिक। प्रश्न पूछता है कि एक उप प्रकार एक विधि के साथ एक विधि को ओवरराइड क्यों नहीं कर सकता है जिसका रिटर्न ओवरराइड की वापसी का एक उप प्रकार है। –

+1

मैं इस तर्क को समझ सकता हूं कि पशु के उपयोगकर्ता को केवल लेग ऑब्जेक्ट्स के बारे में पता होना चाहिए, लेकिन मुझे कम विश्वास है कि यह कुत्ते वर्ग के उपयोगकर्ताओं से छिपा होना चाहिए। – Luke

0

ठीक है, मैं समझता हूं कि मैं सिर्फ कास्ट कर सकता हूं, लेकिन इसका मतलब है कि क्लाइंट को यह जानना है कि कुत्ते के पास डॉगलेग हैं। मैं क्या सोच रहा हूं कि यदि तकनीकी कारण हैं तो यह क्यों संभव नहीं है, यह देखते हुए कि एक अंतर्निहित रूपांतरण मौजूद है।

0

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

0

@Luke

में वर्णन किया गया है मैं अपने शायद गलतफहमी विरासत में सोचते हैं। Dog.GetLeg() एक DogLeg ऑब्जेक्ट वापस करेगा।

public class Dog{ 
    public Leg GetLeg(){ 
     DogLeg dl = new DogLeg(super.GetLeg()); 
     //set dogleg specific properties 
    } 
} 


    Animal a = getDog(); 
    Leg l = a.GetLeg(); 
    l.kick(); 

जिसे वास्तविक विधि कहा जाता है वह कुत्ता होगा। गेटलेग(); और DogLeg.Kick() (मैं एक विधि ले रहा हूँ Leg.kick() मौजूद है) वहां, घोषित रिटर्न प्रकार डॉगलेग असफल है, क्योंकि वह वापस लौटाया गया है, भले ही कुत्ते के लिए वापसी प्रकार। GetLeg() है टांग।

0

आप इंटरफेस आईएलईजी भी वापस कर सकते हैं कि दोनों लेग और/या डॉगलेग लागू होते हैं।

3
abstract class Animal 
{ 
    public virtual Leg GetLeg() 
} 

abstract class Leg { } 

class Dog : Animal 
{ 
    public override Leg GetLeg() { return new DogLeg(); } 
} 

class DogLeg : Leg { void Hump(); } 

इसे इस तरह करते हैं, तो आप अपने ग्राहक में अमूर्त का लाभ उठाने कर सकते हैं:

Leg myleg = myDog.GetLeg(); 

तो अगर आप की जरूरत है, तो आप इसे डाली कर सकते हैं:

if (myleg is DogLeg) { ((DogLeg)myLeg).Hump()); } 

पूरी तरह से काल्पनिक, लेकिन बिंदु यह है कि आप यह कर सकते हैं:

foreach (Animal a in animals) 
{ 
    a.GetLeg().SomeMethodThatIsOnAllLegs(); 
} 

अभी भी Doglegs पर एक विशेष हंप विधि रखने की क्षमता बनाए रखने के दौरान।

1

शायद यह एक उदाहरण के साथ समस्या यह देखना आसान हो जाए:

Animal dog = new Dog(); 
dog.SetLeg(new CatLeg()); 

अब जब कि संकलन चाहिए अगर आप कुत्ता संकलित हैं, लेकिन हम शायद इस तरह के एक उत्परिवर्ती नहीं करना चाहती।

एक संबंधित समस्या कुत्ते [] एक पशु होना चाहिए [], या IList < कुत्ता> एक आईएलआईस्ट < पशु>?

0

यह ध्यान रखना होगा कि आप एक व्युत्पन्न प्रकार हर जगह आप आधार प्रकार का उपयोग

(आप किसी भी विधि/प्रॉपर्टी/क्षेत्र/चर कि पशु की उम्मीद करने के लिए डॉग पारित कर सकते हैं) का उपयोग कर सकते है इस समारोह लेते हैं है :

AddLeg(new Dog()); 

तो संपत्ति Dog.Leg प्रकार के पैर AddLeg समारोह रों नहीं है:

public void AddLeg(Animal a) 
{ 
    a.Leg = new Leg(); 
} 

एक पूरी तरह से वैध कार्य, अब की तरह समारोह कॉल अचानक एक त्रुटि है और संकलित नहीं किया जा सकता है।

2

यह नहीं कि यह बहुत अधिक उपयोग है, लेकिन यह ध्यान रखना दिलचस्प है कि जावा कॉन्वर्स रिटर्न का समर्थन करता है, और इसलिए यह वास्तव में काम करेगा कि आप कैसे उम्मीद करते हैं।स्पष्ट रूप से कि जावा में गुण नहीं हैं;)

12

स्पष्ट रूप से, यदि आप टूटे हुए DogLeg पर चल रहे हैं तो आपको एक कलाकार की आवश्यकता होगी।

+1

हाहाहा शब्दों पर अच्छा खेल :) –

3

आप लागू करने के लिए कि सी # में जेनरिक और इंटरफेस का उपयोग कर सकते हैं:

abstract class Leg { } 

interface IAnimal { Leg GetLeg(); } 

abstract class Animal<TLeg> : IAnimal where TLeg : Leg 
{ public abstract TLeg GetLeg(); 
    Leg IAnimal.GetLeg() { return this.GetLeg(); } 
} 

class Dog : Animal<Dog.DogLeg> 
{ public class DogLeg : Leg { } 
    public override DogLeg GetLeg() { return new DogLeg();} 
} 
+0

ध्यान रखें कि इस सुरुचिपूर्ण दिखने वाले समाधान से सी # की कमी के कारण जटिलताओं का कारण बन सकता है सामान्य भिन्नता समर्थन। –

+0

किस तरह की जटिलताओं? इस समाधान के लिए किसी भिन्नता की आवश्यकता नहीं है। –

+0

क्या होगा यदि हमें अधिक जटिल कुत्ते की आवश्यकता हो? एक जिसमें डॉगटेल, डॉगटेथ, डॉगएटीसी है .. – Seiti

4

यह एक पूरी तरह से वैध हस्ताक्षर एक अधिभावी विधि एक वापसी प्रकार में वापसी प्रकार की एक उप-प्रकार है कि राशि की इच्छा है ओवरराइड विधि (पुhew)। आखिरकार, वे रन-टाइम प्रकार संगत हैं।

लेकिन सी # अभी तक ओवरराइड विधियों में "कॉन्वेंट रिटर्न प्रकार" का समर्थन नहीं करता है (सी ++ [1998] & जावा [2004] के विपरीत)।

आप को हल करने के और निकट भविष्य के लिए करते हैं बनाने के लिए, के रूप में एरिक Lippert his blog में कहा गया है [19 जून 2008] की आवश्यकता होगी:

विचरण इस तरह का "वापसी प्रकार सहप्रसरण" कहा जाता है ।

हमारे पास सी # में उस तरह के भिन्नता को लागू करने की कोई योजना नहीं है।

0

आप प्राप्त कर सकते हैं एक उचित बाधा के साथ एक सामान्य का उपयोग कर, की तरह से आप क्या चाहते हैं निम्नलिखित:

abstract class Leg { } 
class DogLeg : Leg { } 

interface IAnimal 
{ 
    Leg GetLeg(); 
} 

class Dog : IAnimal 
{ 
    public override DogLeg GetLeg() { /* */ } 

    Leg IAnimal.GetLeg() { return GetLeg(); } 
} 

:

abstract class Animal<LegType> where LegType : Leg 
{ 
    public abstract LegType GetLeg(); 
} 

abstract class Leg { } 

class Dog : Animal<DogLeg> 
{ 
    public override DogLeg GetLeg() 
    { 
     return new DogLeg(); 
    } 
} 

class DogLeg : Leg { } 
1

सी # स्पष्ट इंटरफेस कार्यान्वयन सिर्फ इस मुद्दे के समाधान के लिए है यदि आपके पास कुत्ते के संदर्भ के माध्यम से कुत्ता है, तो GetLeg() को कॉल करने से डॉगलेग वापस आ जाएगा। यदि आपके पास एक ही वस्तु है, लेकिन संदर्भ IAnimal प्रकार है, तो यह एक पैर वापस कर देगा।

संबंधित मुद्दे