2008-09-19 2 views
48

मान लीजिए कि मेरे पास सार्वजनिक विधियों ए और बी के साथ बेस क्लास है, और मैं विरासत के माध्यम से DerivedClass बना सकता हूं।सी # - सार्वजनिक रूप से विरासत विधियों को छुपाया जा सकता है (उदाहरण के लिए व्युत्पन्न वर्ग के लिए निजी बनाया गया)

उदा।

public DerivedClass : BaseClass {} 

अब मैं DerivedClass में एक विधि सी ए का उपयोग करता है विकसित करना चाहते हैं और बी वहाँ एक रास्ता मैं तरीकों ए और बी ओवरराइड DerivedClass में निजी होने के लिए कर सकते हैं ताकि केवल विधि सी कोई है जो चाहता है के संपर्क में है है मेरे DerivedClass का उपयोग करने के लिए?

उत्तर

64

यह संभव नहीं है, क्यों?

सी # में, यह आपको मजबूर करता है कि यदि आप सार्वजनिक तरीकों का उत्तराधिकारी हैं, तो आपको उन्हें सार्वजनिक करना होगा। अन्यथा वे उम्मीद करते हैं कि आप कक्षा से पहले स्थान पर न आएं।

एक रिश्ते का उपयोग करने के बजाय, आपको एक रिश्ते का उपयोग करना होगा।

भाषा डिजाइनर इस उद्देश्य को अनुमति नहीं देते हैं ताकि आप विरासत का अधिक उचित उपयोग कर सकें।

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

इसलिए वे इसे खराब विरासत पदानुक्रमों से बचाने के लिए अनुमति नहीं देते हैं।

इसके बजाय आपको क्या करना चाहिए?

इसके बजाय आपको इंटरफेस लागू करना चाहिए। इससे आपको रिश्ते का उपयोग करके कार्यक्षमता मिलती है।

अन्य भाषाओं:

C++ में आप बस एक आपरिवर्तक के आधार वर्ग से पहले निर्दिष्ट करते हैं, निजी सार्वजनिक या रक्षा की। यह उस आधार के सभी सदस्यों को बनाता है जो उस निर्दिष्ट पहुंच स्तर पर सार्वजनिक थे। यह मुझे मूर्खतापूर्ण लगता है कि आप सी # में ऐसा नहीं कर सकते हैं।

पुनर्गठन कोड:

दूसरों का सुझाव दिया है एक बनाने के लिए:

interface I 
{ 
    void C(); 
} 

class BaseClass 
{ 
    public void A() { MessageBox.Show("A"); } 
    public void B() { MessageBox.Show("B"); } 
} 

class Derived : I 
{ 
    public void C() 
    { 
     b.A(); 
     b.B(); 
    } 

    private BaseClass b; 
} 

मैं ऊपर की कक्षाओं के नाम को समझने के लिए थोड़ा विवादास्पद :)

अन्य सुझाव दिए गए हैं() और बी() सार्वजनिक और अपवाद फेंक देते हैं। लेकिन यह लोगों के उपयोग के लिए एक दोस्ताना वर्ग नहीं बनाता है और यह वास्तव में समझ में नहीं आता है।

+0

के बजाय इंटरफ़ेस का उपयोग करता है अच्छी तरह से लिखित उत्तर। –

+0

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

+0

सार्वजनिक तरीकों को छिपाने का एक अन्य कारण यह है कि यह उपभोक्ताओं के साथ इंटरफेस अनुबंध तोड़ सकता है। उदाहरण के लिए, आईएलआईस्ट लागू करने वाली बेस क्लास से निकालें() विधि को छिपाने से इंटरफ़ेस के आधार पर मौजूद होने वाली विधि को किसी भी कोड को तोड़ दिया जाएगा। –

0

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

संपादित करें: जॉर्ज फेरेरा सही है।

3

ऐसा करने का एकमात्र तरीका है जो मुझे पता है कि हैस-ए रिलेशनशिप का उपयोग करना है और केवल उन कार्यों को लागू करना है जिन्हें आप बेनकाब करना चाहते हैं।

1

@ ब्रायन आर बॉन्डी ने मुझे विरासत के माध्यम से छिपाने पर एक दिलचस्प लेख और नया कीवर्ड बताया।

http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx

तो समाधान के रूप में मेरा सुझाव है:

DerivedClass d = new DerivedClass(); 
    d.A(); 
+1

आप DerivedClass d = नए DerivedClass का उपयोग करते हैं(); डीए() आप अभी भी बेस क्लास 'ए() कार्यान्वयन तक पहुंच पाएंगे। –

+0

जॉर्ज, मैंने अभी कोशिश की और ब्रायन ने बताया कि ए और बी अभी भी सामने आएगा। – Lyndon

+0

http://msdn.microsoft.com/en-us/library/aa691135(VS.71).aspx –

6

कि एक बुरा विचार की तरह लगता है:

इस तरह
class BaseClass 
{ 
    public void A() 
    { 
     Console.WriteLine("BaseClass.A"); 
    } 

    public void B() 
    { 
     Console.WriteLine("BaseClass.B"); 
    } 
} 

class DerivedClass : BaseClass 
{ 
    new public void A() 
    { 
     throw new NotSupportedException(); 
    } 

    new public void B() 
    { 
     throw new NotSupportedException(); 
    } 

    public void C() 
    { 
     base.A(); 
     base.B(); 
    } 
} 

इस तरह कोड एक NotSupportedException फेंक देते हैं। Liskov प्रभावित नहीं होंगे।

यदि आप नहीं चाहते हैं कि DerivedClass के उपभोक्ता विधियों तक पहुंचने में सक्षम हों DeriveClass.A() और DerivedClass.B() मैं सुझाव दूंगा कि DerivedClass को कुछ सार्वजनिक इंटरफ़ेस IWhateverMethodCIs लागू करना चाहिए और DerivedClass के उपभोक्ताओं को वास्तव में बात करनी चाहिए IWhateverMethodCIs के बारे में और बेसक्लास या DerivedClass के कार्यान्वयन के बारे में कुछ भी नहीं पता है।

+0

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

+0

लिंडन, उम्मीद है कि स्वीकृत उत्तर में कोड यह स्पष्ट करता है कि DerivedClass अभी भी बेस क्लास का उप-वर्ग है और आप अभी भी सी को लागू करने के लिए DerivedClass में फ़ंक्शन ए और बी का उपयोग कर सकते हैं लेकिन बाहरी दुनिया DerivedClass के साथ DerivedClass के रूप में बातचीत नहीं कर पाती है और –

1

छुपा एक सुंदर फिसलन ढलान है। मुख्य मुद्दों, IMO, कर रहे हैं:

  • यह उदाहरण के डिजाइन समय घोषणा प्रकार पर निर्भर है, अगर आप BaseClass obj = नया उपवर्ग (जैसे कुछ करना) जिसका अर्थ है, तो कॉल obj। ए(), छिपाना पराजित है। BaseClass.A() निष्पादित किया जाएगा।

  • छिपाने व्यवहार (या व्यवहार में परिवर्तन) आधार प्रकार में बहुत आसानी से अस्पष्ट हो सकता है। यह स्पष्ट रूप से चिंता का विषय है जब आप समीकरण के दोनों पक्षों के स्वामी हैं, या यदि 'base.xxx' को कॉल करना आपके उप-सदस्य का हिस्सा है।

  • यदि आप वास्तव में आधार/उप-वर्ग समीकरण के दोनों पक्षों के अपने पक्ष हैं, तो आप संस्थागत छिपाने/छायांकन से अधिक प्रबंधनीय समाधान तैयार करने में सक्षम होना चाहिए।
1

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

एक आगामी कोडिंग प्रतिमान को "विरासत पर संरचना" कहा जाता है। यह वस्तु-उन्मुख विकास (विशेष रूप से एकल उत्तरदायित्व सिद्धांत और खुले/बंद सिद्धांत) के सिद्धांतों से सीधे खेलता है।

दुर्भाग्यवश, जिस तरह से हम में से बहुत से डेवलपर्स को ऑब्जेक्ट-ओरिएंटेशन सिखाया गया था, हमने रचना के बजाय विरासत के बारे में तुरंत सोचने की आदत बनाई है। हमारे पास बड़ी कक्षाएं होती हैं जिनके पास कई अलग-अलग जिम्मेदारियां होती हैं क्योंकि वे एक ही "असली दुनिया" वस्तु के साथ निहित हो सकती हैं। यह वर्ग पदानुक्रमों का कारण बन सकता है जो 5+ स्तर गहरे हैं।

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

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

मैं एक प्रणाली के साथ सौदा करने के बजाय सैकड़ों/हजारों/यहां तक ​​कि अधिक कक्षाओं के साथ एक प्रणाली को बनाए रखने के बजाय बहुत अधिक होगा जो पॉलिमॉर्फिज्म/विरासत का भारी उपयोग करता है और इसमें कम वर्ग होते हैं जो अधिक कसकर होते हैं युग्मित।

शायद ऑब्जेक्ट उन्मुख विकास पर संसाधन संसाधन रॉबर्ट सी मार्टिन की पुस्तक, Agile Software Development, Principles, Patterns, and Practices है।

5

आपको जो चाहिए वह संरचना विरासत नहीं है।

class Plane 
{ 
    public Fly() { .. } 
    public string GetPilot() {...} 
} 

अब आप विमान की एक विशेष प्रकार का, PairOfWings = 2 है, लेकिन अन्यथा सब कुछ एक विमान कर सकते हैं करता है कि इस तरह के रूप में एक की जरूरत है .. आप हवाई जहाज के वारिस। इसके द्वारा आप घोषणा करते हैं कि आपका व्युत्पन्न आधार वर्ग के अनुबंध को पूरा करता है और जहां भी बेस क्लास की अपेक्षा की जाती है वहां झपकी के बिना प्रतिस्थापित किया जा सकता है। जैसे लॉगफलाइट (विमान) एक द्विपक्षीय उदाहरण के साथ काम करना जारी रखेगा।

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

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

+1

बेस क्लास विधियों और गुणों को छिपाने की आवश्यकता तब होती है जब आपके पास बेस क्लास के स्रोत पर नियंत्रण नहीं होता है। मान लीजिए कि आपको बेस क्लास की पूरी प्रणाली को ओवरराइड करना है जो आपकी ज़रूरतों को पूरा नहीं करता है। आप अपनी खुद की विधियों को बनाते हैं, दूसरों को ओवरराइड करते हैं लेकिन मूल तंत्र से संबंधित कुछ छिपाना भी चाहते हैं और जो अन्य प्रोग्रामर द्वारा उपयोग किया जा सकता है। आप "आधार" वर्ग को एकत्रीकरण/लपेटना चुन सकते हैं, लेकिन यह हमेशा स्वीकार्य नहीं है क्योंकि यह विरासत श्रृंखला को तोड़ता है। अप्रचलित के रूप में ऐसी विधियों को चिह्नित करना सबसे अच्छा sollution –

16

जब आप, उदाहरण के लिए, एक List<object> से विरासत की कोशिश करते हैं, और आप प्रत्यक्ष Add(object _ob) सदस्य छुपाना चाहते हैं:

// the only way to hide 
[Obsolete("This is not supported in this class.", true)] 
public new void Add(object _ob) 
{ 
    throw NotImplementedException("Don't use!!"); 
} 

यह वास्तव में सबसे बेहतर समाधान नहीं है, लेकिन यह काम करता है।IntelliSense अभी भी स्वीकार करता है, लेकिन संकलन समय पर आपको कोई त्रुटि मिलती:

error CS0619: 'TestConsole.TestClass.Add(TestConsole.TestObject)' is obsolete: 'This is not supported in this class.'

+1

सुंदर है! यही मुझे चाहिए। मुझे एक बुरी तरह से संरचित बुरी कक्षा मिली है जिसे मैं नहीं बदल सकता (किसी और के स्वामित्व में) जिसके पास सदस्यों को छिपाने की जरूरत है। यह बहुत अच्छा काम करता है। ध्यान दें कि सी ++ आपको ऐसा करने देता है। –

0

जबकि सवाल का जवाब "नहीं", वहाँ एक टिप मैं यहाँ आ रहा है दूसरों के लिए बाहर बिंदु करना चाहते है (यह देखते हुए कि ओ पी रहा था तीसरे पक्ष द्वारा असेंबली पहुंच के लिए संकेत देने की तरह)। दूसरों एक विधानसभा का संदर्भ है, दृश्य स्टूडियो निम्न विशेषता का सम्मान तो यह IntelliSense में नहीं दिखाया जाएगा होना चाहिए (छिपा हुआ, लेकिन अभी भी कहा जा सकता है, इसलिए सावधान रहना):

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] 

यदि आप कोई विकल्प नहीं था, तो आप चाहिए बेस टाइप विधि को छिपाने वाली विधि पर new का उपयोग करने में सक्षम होने के लिए, => throw new NotSupportedException(); वापस करें, और इसे उपरोक्त विशेषता के साथ संयोजित करें।

एक और चाल बेस क्लास से विरासत में प्राप्त होने पर निर्भर करती है, जहां बेस के पास एक समान इंटरफ़ेस है (जैसे IList<T>List<T> के लिए)। इंटरफेस को "स्पष्ट रूप से" कार्यान्वित करना कक्षा के प्रकार पर इंटेलिजेंस से उन विधियों को भी छिपाएगा। उदाहरण के लिए:

public class GoodForNothing: IDisposable 
{ 
    void IDisposable.Dispose() { ... } 
} 

var obj = new GoodForNothing() के मामले में, Dispose() विधि obj पर उपलब्ध नहीं होगा। हालांकि, यह किसी भी व्यक्ति के लिए उपलब्ध होगा जो स्पष्ट रूप से टाइप करता है objIDisposable पर।

इसके अलावा, आप भी एक आधार प्रकार बजाय इसे से इनहेरिट की लपेट सकता है, तो कुछ तरीकों को छिपाने:

public class MyList<T> : IList<T> 
{ 
    List<T> _Items = new List<T>(); 
    public T this[int index] => _Items[index]; 
    public int Count => _Items.Count; 
    public void Add(T item) => _Items.Add(item); 
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] 
    void ICollection<T>.Clear() => throw new InvalidOperationException("No you may not!"); // (hidden) 
    /*...etc...*/ 
} 
संबंधित मुद्दे