चूंकि आपने पूछा कि C # ने ऐसा क्यों किया, इसलिए C # रचनाकारों से पूछना सबसे अच्छा है। सी # के प्रमुख वास्तुकार एंडर्स हेजलसबर्ग ने उत्तर दिया कि उन्होंने एक साक्षात्कार में डिफ़ॉल्ट रूप से वर्चुअल (जावा के अनुसार) के साथ जाने का फैसला क्यों नहीं किया , प्रासंगिक स्निपेट्स नीचे हैं।
ध्यान रखें कि एक विधि को गैर-आभासी के रूप में चिह्नित करने के लिए जावा में डिफ़ॉल्ट रूप से अंतिम कीवर्ड के साथ आभासी है। अभी भी सीखने के लिए दो अवधारणाएं हैं, लेकिन बहुत से लोग अंतिम कीवर्ड के बारे में नहीं जानते हैं या सटीक रूप से उपयोग नहीं करते हैं। C # एक को आभासी और नए / ओवरराइड का उपयोग करने के लिए सचेत रूप से उन निर्णय लेने के लिए मजबूर करता है।
इसके कई कारण हैं। एक प्रदर्शन है । हम यह देख सकते हैं कि जैसे ही लोग जावा में कोड लिखते हैं, वे अपने तरीकों को अंतिम रूप देना भूल जाते हैं। इसलिए, वे विधियां आभासी हैं। क्योंकि वे आभासी हैं, वे भी प्रदर्शन नहीं करते हैं। वहाँ सिर्फ एक आभासी विधि होने के साथ जुड़े प्रदर्शन ओवरहेड है। यह एक मुद्दा है।
एक अधिक महत्वपूर्ण मुद्दा संस्करण है । आभासी तरीकों के बारे में विचार के दो स्कूल हैं। अकादमिक स्कूल ऑफ़ थिंक कहते हैं, "सब कुछ आभासी होना चाहिए, क्योंकि मैं किसी दिन इसे ओवरराइड करना चाहता हूं।" विचार का व्यावहारिक स्कूल, जो वास्तविक दुनिया में चलने वाले वास्तविक अनुप्रयोगों के निर्माण से आता है, कहते हैं, "हम जो हम आभासी बनाते हैं उसके बारे में वास्तविक सावधान रहना होगा।"
जब हम किसी प्लेटफ़ॉर्म में कुछ वर्चुअल करते हैं, तो हम भविष्य में कैसे विकसित होते हैं, इस बारे में बहुत सारे वादे करते हैं। एक गैर-आभासी पद्धति के लिए, हम वादा करते हैं कि जब आप इस विधि को कॉल करेंगे, तो x और y होगा। जब हम एक एपीआई में एक आभासी विधि प्रकाशित करते हैं, तो हम न केवल यह वादा करते हैं कि जब आप इस पद्धति को कॉल करेंगे, तो x और y होगा। हम यह भी वादा करते हैं कि जब आप इस पद्धति को ओवरराइड करते हैं, तो हम इसे अन्य लोगों के संबंध में इस विशेष क्रम में कहेंगे और राज्य इस और उस अपरिवर्तनवादी स्थिति में होगा।
हर बार जब आप किसी API में वर्चुअल कहते हैं, तो आप कॉल बैक हुक बना रहे होते हैं। OS या API फ्रेमवर्क डिज़ाइनर के रूप में, आपको इसके बारे में वास्तविक सावधान रहना होगा। आप नहीं चाहते कि उपयोगकर्ता किसी API में किसी भी मनमानी बिंदु पर ओवरराइडिंग और हुकिंग करें, क्योंकि आप आवश्यक रूप से उन वादों को पूरा नहीं कर सकते हैं। और लोग उन वादों को पूरी तरह से नहीं समझ सकते हैं जब वे कुछ आभासी बनाते हैं।
साक्षात्कार में इस बारे में अधिक चर्चा है कि डेवलपर्स वर्ग की विरासत डिजाइन के बारे में कैसे सोचते हैं, और यह कैसे उनके निर्णय का कारण बना।
अब निम्नलिखित प्रश्न पर:
मुझे यह समझ में नहीं आ रहा है कि दुनिया में मैं अपने DerivedClass में उसी नाम और BaseClass के समान हस्ताक्षर के साथ एक विधि जोड़ने जा रहा हूं और एक नए व्यवहार को परिभाषित करूंगा लेकिन रन-टाइम बहुरूपता पर, BaseClass विधि को लागू किया जाएगा! (जो ओवरराइडिंग नहीं है लेकिन तार्किक रूप से यह होना चाहिए)।
यह तब होगा जब एक व्युत्पन्न वर्ग यह घोषित करना चाहता है कि वह आधार वर्ग के अनुबंध का पालन नहीं करता है, लेकिन उसी नाम से एक विधि है। (जो कोई बीच का अंतर पता नहीं है के लिए new
और override
सी # में, यह देखने MSDN पेज )।
एक बहुत ही व्यावहारिक परिदृश्य यह है:
- आपने एक एपीआई बनाया, जिसमें एक वर्ग है जिसे बुलाया गया है
Vehicle
।
- मैंने आपके एपीआई और व्युत्पन्न का उपयोग करना शुरू कर दिया
Vehicle
।
- आपकी
Vehicle
कक्षा के पास कोई विधि नहीं थी PerformEngineCheck()
।
- अपनी
Car
कक्षा में, मैं एक विधि जोड़ता हूं PerformEngineCheck()
।
- आपने अपने एपीआई का एक नया संस्करण जारी किया और एक जोड़ा
PerformEngineCheck()
।
- मैं अपने तरीके का नाम नहीं बदल सकता क्योंकि मेरे ग्राहक मेरे एपीआई पर निर्भर हैं, और यह उन्हें तोड़ देगा।
इसलिए जब मैं आपके नए एपीआई के खिलाफ प्रतिक्रिया करता हूं, तो C # मुझे इस मुद्दे की चेतावनी देता है, उदा
यदि आधार PerformEngineCheck()
नहीं था virtual
:
app2.cs(15,17): warning CS0108: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'.
Use the new keyword if hiding was intended.
और अगर आधार PerformEngineCheck()
था virtual
:
app2.cs(15,17): warning CS0114: 'Car.PerformEngineCheck()' hides inherited member 'Vehicle.PerformEngineCheck()'.
To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
अब, मुझे स्पष्ट रूप से यह निर्णय लेना चाहिए कि क्या मेरी कक्षा वास्तव में आधार वर्ग के अनुबंध का विस्तार कर रही है, या यदि यह एक अलग अनुबंध है, लेकिन एक ही नाम से होता है।
- इसे बनाने से
new
, मैं अपने ग्राहकों को नहीं तोड़ता अगर आधार विधि की कार्यक्षमता व्युत्पन्न विधि से भिन्न होती। संदर्भित किसी भी कोड को बुलाया Vehicle
नहीं देखा जाएगा Car.PerformEngineCheck()
, लेकिन जिस कोड का संदर्भ था Car
, उसी कार्यक्षमता को देखता रहेगा जो मैंने पेश किया था PerformEngineCheck()
।
इसी तरह का एक उदाहरण है जब बेस क्लास में एक अन्य विधि कॉलिंग हो सकती है PerformEngineCheck()
(नए संस्करण में esp), कोई इसे PerformEngineCheck()
व्युत्पन्न वर्ग के कॉल को कैसे रोक सकता है ? जावा में, वह निर्णय बेस क्लास के साथ आराम करेगा, लेकिन यह व्युत्पन्न वर्ग के बारे में कुछ भी नहीं जानता है। C # में, वह निर्णय दोनों आधार वर्ग ( virtual
कीवर्ड के माध्यम से ), और व्युत्पन्न वर्ग ( new
और override
कीवर्ड के माध्यम से ) पर टिकी हुई है ।
बेशक, त्रुटियाँ जो कंपाइलर फेंकता है, वे प्रोग्रामर को अप्रत्याशित रूप से त्रुटियां न करने के लिए एक उपयोगी उपकरण प्रदान करते हैं (इसलिए या तो बिना देखे या बिना महसूस किए नई कार्यक्षमता प्रदान करते हैं।)
जैसा कि एंडर्स ने कहा, वास्तविक दुनिया हमें ऐसे मुद्दों पर मजबूर करती है, जो अगर हम खरोंच से शुरू करते हैं, तो हम कभी नहीं चाहेंगे।
संपादित करें: new
इंटरफ़ेस संगतता सुनिश्चित करने के लिए कहां उपयोग किया जाएगा इसका एक उदाहरण जोड़ा गया ।
EDIT: टिप्पणियों के माध्यम से जाने के दौरान, मैं एरिक लिपर्ट (तब C # डिज़ाइन समिति के सदस्यों में से एक) द्वारा अन्य उदाहरण परिदृश्यों (ब्रायन द्वारा उल्लिखित) पर भी लिखा गया।
भाग 2: अद्यतन प्रश्न पर आधारित
लेकिन C # के मामले में अगर SpaceShip ने वाहन वर्ग की गति को ओवरराइड नहीं किया और नए का उपयोग किया तो मेरे कोड का तर्क टूट जाएगा। क्या यह नुकसान नहीं है?
कौन तय करता है कि क्या SpaceShip
वास्तव में ओवरराइडिंग है Vehicle.accelerate()
या यदि यह अलग है? उसे SpaceShip
डेवलपर बनना होगा । इसलिए यदि SpaceShip
डेवलपर यह निर्णय लेता है कि वे आधार वर्ग का अनुबंध नहीं रख रहे हैं, तो आपकी कॉल को Vehicle.accelerate()
नहीं जाना SpaceShip.accelerate()
चाहिए या इसे नहीं करना चाहिए? जब वे इसे चिह्नित करेंगे new
। हालांकि, अगर वे तय करते हैं कि यह वास्तव में अनुबंध रखता है, तो वे वास्तव में इसे चिह्नित करेंगे override
। किसी भी स्थिति में, आपका कोड अनुबंध के आधार पर सही पद्धति को कॉल करके सही ढंग से व्यवहार करेगा । आपका कोड कैसे तय कर सकता है कि क्या SpaceShip.accelerate()
वास्तव में ओवरराइडिंग है Vehicle.accelerate()
या यह नाम टकराव है? (ऊपर मेरा उदाहरण देखें)।
हालांकि, निहित विरासत के मामले में, भले ही SpaceShip.accelerate()
नहीं रख था अनुबंध की Vehicle.accelerate()
, विधि कॉल अभी भी करने के लिए जाना होगा SpaceShip.accelerate()
।