हमें नए कीवर्ड की आवश्यकता क्यों है और डिफ़ॉल्ट को छिपाने और ओवरराइड न करने का व्यवहार क्यों है?


81

मैं इस ब्लॉग पोस्ट को देख रहा था और निम्नलिखित प्रश्न थे:

  • हमें newकीवर्ड की आवश्यकता क्यों है , क्या यह निर्दिष्ट करना है कि बेस क्लास पद्धति छिपाई जा रही है। मेरा मतलब है, हमें इसकी आवश्यकता क्यों है? यदि हम overrideकीवर्ड का उपयोग नहीं करते हैं, तो क्या हम बेस क्लास पद्धति को नहीं छिपा रहे हैं?
  • ओवर # में डिफ़ॉल्ट क्यों छुपाया जाता है और ओवरराइड नहीं होता है? डिजाइनरों ने इसे इस तरह से क्यों लागू किया है?

6
थोड़ा और दिलचस्प सवाल (मेरे लिए) है कि क्यों छिपाना बिल्कुल कानूनी है। यह आमतौर पर एक त्रुटि है (इसलिए संकलक चेतावनी), शायद ही कभी चाहता था और हमेशा सबसे अच्छा समस्याग्रस्त।
कोनराड रुडोल्फ

जवाबों:


127

अच्छे प्रश्न हैं। मैं उन्हें फिर से बताऊं।

किसी विधि को किसी अन्य विधि से छिपाना क्यों कानूनी है?

उस प्रश्न का उत्तर एक उदाहरण से देता हूं। आपके पास CLR v1 से एक इंटरफ़ेस है:

interface IEnumerable
{
    IEnumerator GetEnumerator();
}

उत्तम। अब CLR में v2 आप जेनरिक है और आपको लगता है "आदमी, अगर v1 में केवल हम था था जेनरिक मैं इस एक सामान्य इंटरफ़ेस बना सकता था। लेकिन मैं नहीं। मैं अब इसके साथ संगत कुछ है कि बनाना चाहिए था है सामान्य ताकि मुझे कोड के साथ पीछे की संगतता खोने के बिना जेनरिक का लाभ मिलता है जो IEnumerable की अपेक्षा करता है। "

interface IEnumerable<T> : IEnumerable
{
    IEnumerator<T> .... uh oh

क्या आप GetEnumerator विधि को कॉल करने जा रहे हैं IEnumerable<T>? याद रखें, आप चाहते हैं कि यह गैर-जेनेरिक आधार इंटरफेस पर गेटइन्यूमर को छिपा दे। आप कभी भी उस चीज को नहीं चाहते हैं जब तक कि आप स्पष्ट रूप से एक बैकवर्ड-कॉम्पिटिशन की स्थिति में न हों।

यह अकेला विधि छिपाने का औचित्य सिद्ध करता है। पद्धति छिपाने के औचित्य पर अधिक विचार के लिए विषय पर मेरा लेख देखें ।

"नया" के बिना छिपाना एक चेतावनी का कारण क्यों बनता है?

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

एक त्रुटि के बजाय "नया" चेतावनी के बिना क्यों छिपाया जाता है?

इसी कारण से। हो सकता है कि आप गलती से कुछ छिपा रहे हों, क्योंकि आपने बस एक बेस क्लास का नया संस्करण उठाया है। यह हमेशा होता है। FooCorp एक बेस क्लास बनाता है। B. BarCorp एक व्युत्पन्न क्लास D को एक मेथड बार के साथ बनाता है, क्योंकि उनके ग्राहक उस तरीके को पसंद करते हैं। फूकोर्प देखता है और कहता है कि, यह एक अच्छा विचार है, हम उस कार्यक्षमता को आधार वर्ग पर रख सकते हैं। वे ऐसा करते हैं और Foo.DLL का एक नया संस्करण शिप करते हैं, और जब BarCorp नया संस्करण चुनता है, तो अच्छा होगा यदि उन्हें बताया जाए कि उनका तरीका अब बेस क्लास पद्धति को छुपाता है।

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

क्यों छुपा है और डिफ़ॉल्ट को ओवरराइड नहीं कर रहा है?

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


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

@ कोनराड: मैं मानता हूं कि पिछड़ेपन की वजह से नुकसान हो सकता है। मोनो ने भविष्य के रिलीज में 1.1 के लिए समर्थन छोड़ने का फैसला किया है।
सिमेंडोजो

1
@Konrad: अपनी भाषा के साथ पिछड़ी अनुकूलता को तोड़ना अपनी भाषा का उपयोग करने वाले लोगों के लिए अपने स्वयं के कोड के साथ पश्चगामी संगतता को तोड़ने के मुकाबले आसान है। एरिक का उदाहरण दूसरा मामला है। और उस कोड को लिखने वाले व्यक्ति ने एक निर्णय लिया है कि वह पीछे की संगतता चाहता है। यदि संभव हो तो भाषा को उस निर्णय का समर्थन करना चाहिए।
ब्रायन

ईश्वर उत्तर, एरिक। हालाँकि, एक और अधिक विशिष्ट कारण है कि डिफ़ॉल्ट रूप से ओवरराइडिंग खराब है। यदि एक बेस क्लास बेस एक नई विधि डक () (जो एक डक ऑब्जेक्ट देता है) का परिचय देता है, जो कि आपके मौजूदा विधि डक () के रूप में एक ही हस्ताक्षर करने के लिए होता है (जो मारियो डक बनाता है) एक व्युत्पन्न वर्ग में व्युत्पन्न (यानी कोई संचार नहीं है) आप और बेस के लेखक), आप नहीं चाहते कि दोनों वर्गों के ग्राहक मारियो को बत्तख बनाने के लिए जब वे सिर्फ एक बतख चाहते हैं।
ज्ञानदेव

सारांश में, यह ज्यादातर बदलते वर्ग और इंटरफेस के साथ करना है।
ज्ञानदेव

15

यदि व्युत्पन्न वर्ग में विधि नए खोजशब्द से पहले है, तो विधि को आधार वर्ग में विधि से स्वतंत्र होने के रूप में परिभाषित किया गया है

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

तो यह आपको गलतियों से बचने में मदद करता है और स्पष्ट रूप से दिखाता है कि आप क्या करना चाहते हैं और यह अधिक पठनीय कोड बनाता है, इसलिए कोई भी आपके कोड को आसानी से समझ सकता है।


12

यह ध्यान देने योग्य है कि इस संदर्भ में एकमात्र प्रभाव newएक चेतावनी को दबाने के लिए है। शब्दार्थ में कोई परिवर्तन नहीं है।

तो एक उत्तर है: हमें newसंकलक को संकेत देने की आवश्यकता है कि छिपाना जानबूझकर और चेतावनी से छुटकारा पाने के लिए है।

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

एकमात्र अच्छा कारण मैं जानबूझकर छिपने के लिए सोच सकता हूं, जब एक इंटरफेस द्वारा एक नाम आप पर मजबूर किया जाता है।


6

C # में सदस्यों को डिफ़ॉल्ट रूप से सील कर दिया जाता है, ताकि आप उन्हें (जब तक virtualया abstractकीवर्ड के साथ चिह्नित नहीं किया जाता) ओवरराइड नहीं कर सकें और यह प्रदर्शन कारणों से होनए संशोधक स्पष्ट रूप से एक विरासत सदस्य को छिपाने के लिए किया जाता है।


4
लेकिन आप वास्तव में नए संशोधक का उपयोग कब करना चाहते हैं?
सिमेंडोजो

1
जब आप बेस क्लास से विरासत में प्राप्त सदस्य को स्पष्ट रूप से छिपाना चाहते हैं। विरासत में दिए गए सदस्य को छिपाने का मतलब है कि सदस्य का व्युत्पन्न संस्करण बेस-क्लास संस्करण को बदल देता है।
डारिन दिमित्रोव

3
लेकिन बेस क्लास का उपयोग करने वाले किसी भी तरीके को अभी भी आधार कार्यान्वयन कहा जाएगा, न कि व्युत्पन्न। यह मुझे एक खतरनाक स्थिति लगती है।
सिमेंडोजो

@simendjo, @Darin दिमित्रोव: मैं भी एक कठिन समय का उपयोग करने के मामले में सोच रहा हूं, जहां नए कीवर्ड की वास्तव में आवश्यकता है। यह मुझे लगता है कि हमेशा बेहतर तरीके होते हैं। मुझे यह भी आश्चर्य है कि क्या बेस क्लास कार्यान्वयन को छिपाना एक डिज़ाइन दोष नहीं है जो आसानी से त्रुटियों की ओर जाता है।
डर्क वोल्मार

2

यदि overrideकीवर्ड निर्दिष्ट किए बिना ओवरराइडिंग डिफ़ॉल्ट थी , तो आप नाम समानता के कारण गलती से अपने आधार की कुछ विधि को ओवरराइड कर सकते हैं।

.Net संकलक की रणनीति चेतावनी का उत्सर्जन करने के लिए है अगर कुछ गलत हो सकता है, बस सुरक्षित होने के लिए, इसलिए इस मामले में यदि ओवरराइडिंग डिफ़ॉल्ट थी, तो प्रत्येक ओवरराइड विधि के लिए एक चेतावनी होनी चाहिए - कुछ ऐसी 'चेतावनी: जाँचें कि क्या आप वास्तव में चाहते हैं उल्लंघन करने के लिए'।


0

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

अपडेटेड ... एरिक ने मुझे इस उदाहरण को बेहतर बनाने के बारे में एक विचार दिया।

public interface IAction1
{
    int DoWork();
}
public interface IAction2
{
    string DoWork();
}

public class MyBase : IAction1
{
    public int DoWork() { return 0; }
}
public class MyClass : MyBase, IAction2
{
    public new string DoWork() { return "Hi"; }
}

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        var ret0 = myClass.DoWork(); //Hi
        var ret1 = ((IAction1)myClass).DoWork(); //0
        var ret2 = ((IAction2)myClass).DoWork(); //Hi
        var ret3 = ((MyBase)myClass).DoWork(); //0
        var ret4 = ((MyClass)myClass).DoWork(); //Hi
    }
}

1
कई इंटरफेस का उपयोग करते समय, आप टकराव से बचने के लिए इंटरफ़ेस विधियों को स्पष्ट रूप से नाम दे सकते हैं। मुझे समझ में नहीं आया कि "एक वर्ग के लिए अलग-अलग कार्यान्वयन" से आपका क्या मतलब है? दोनों के पास एक ही हस्ताक्षर है ...
simendsjo

newआप भी उस बिंदु से बच्चों के सभी के लिए विरासत आधार को बदलने की अनुमति कीवर्ड।
मैथ्यू Whited

-1

जैसा कि कहा गया है, विधि / संपत्ति छिपाना एक ऐसी विधि या संपत्ति के बारे में चीजों को बदलना संभव बनाता है जिसे अन्यथा आसानी से नहीं बदला जा सकता था। एक स्थिति जहां यह उपयोगी हो सकती है, एक विरासत वाले वर्ग को पढ़ने-लिखने के गुण रखने की अनुमति देता है जो केवल आधार वर्ग में ही पढ़े जाते हैं। उदाहरण के लिए, मान लीजिए कि एक बेस क्लास में रीड-ओनली प्रॉपर्टीज़ का एक समूह है, जिसे Value1-Value40 कहा जाता है (निश्चित रूप से, एक वास्तविक वर्ग बेहतर नामों का उपयोग करेगा)। इस वर्ग के एक सील वंशज में एक रचनाकार होता है जो बेस क्लास की एक वस्तु लेता है और वहां से मूल्यों की प्रतिलिपि बनाता है; वर्ग उन्हें इसके बाद बदलने की अनुमति नहीं देता है। एक अलग, अंतर्निहित, वंशज, वैल्यू 1-वैल्यू 40 नामक रीड-राइट गुणों की घोषणा करता है, जिसे जब पढ़ा जाता है, बेस क्लास संस्करणों के समान व्यवहार करता है, लेकिन जब लिखा जाता है, तो मूल्यों को लिखने की अनुमति देता है।

इस दृष्टिकोण के साथ एक झुंझलाहट - शायद कोई मेरी मदद कर सकता है - क्या मुझे छाया के लिए रास्ता नहीं पता है और एक ही वर्ग के भीतर एक विशेष संपत्ति को ओवरराइड करता है। क्या सीएलआर भाषाओं में से कोई भी अनुमति देता है (मैं vb 2005 का उपयोग करता हूं)? यह उपयोगी होगा यदि बेस क्लास ऑब्जेक्ट और उसके गुण सार हो सकते हैं, लेकिन इससे पहले कि एक वंशज वर्ग उन्हें छाया दे सके, उन्हें Value1 से Value40 गुणों तक ओवरराइड करने के लिए एक मध्यवर्ती वर्ग की आवश्यकता होगी।

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