जावा के विपरीत "नया" और "वर्चुअल + ओवरराइड" कीवर्ड के साथ C # क्यों बनाया गया था?


61

जावा में कोई नहीं है virtual, new, overrideविधि परिभाषा के लिए कीवर्ड। तो एक विधि का काम समझना आसान है। कारण यदि DerivedClass फैली BaseClass और एक ही नाम और का एक ही हस्ताक्षर के साथ एक विधि है BaseClass तो अधिभावी रन-टाइम बहुरूपता में जगह ले जाएगा (बशर्ते तरीका नहीं है static)।

BaseClass bcdc = new DerivedClass(); 
bcdc.doSomething() // will invoke DerivedClass's doSomething method.

अब C # पर आते हैं, यह समझने के लिए कि newया virtual+deriveया + वर्चुअल ओवरराइड कैसे काम कर रहा है, इतना भ्रम और कठिन हो सकता है।

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

इस मामले में virtual + overrideहालांकि तार्किक कार्यान्वयन सही है, लेकिन प्रोग्रामर को यह सोचना होगा कि कोडिंग के समय उपयोगकर्ता को किस विधि को ओवरराइड करने की अनुमति देनी चाहिए। जिसके पास कुछ प्रो-कोन हैं (चलो अब वहाँ नहीं जाते हैं)।

तो क्यों सी # में के लिए बहुत बहुत अंतरिक्ष देखते हैं संयुक्त राष्ट्र -logical तर्क और भ्रम की स्थिति। इसलिए मैं जो असली दुनिया के संदर्भ में के रूप में मेरे सवाल reframe सकता है मैं उपयोग की सोचना चाहिए virtual + overrideकी बजाय newऔर भी का उपयोग newकरने के बजाय virtual + override?


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

अब मेरे मन में एक सवाल आया। जावा में जैसे कि मेरे पास एक कोड था

Vehicle vehicle = new Car();
vehicle.accelerate();

और बाद में मैं नए वर्ग SpaceShipसे आया हूं Vehicle। फिर मैं सभी carको एक SpaceShipऑब्जेक्ट में बदलना चाहता हूं मुझे बस कोड की एक पंक्ति को बदलना होगा

Vehicle vehicle = new SpaceShip();
vehicle.accelerate();

इससे कोड के किसी भी बिंदु पर मेरा कोई तर्क नहीं टूटेगा।

लेकिन C # के मामले में अगर 'क्लास को SpaceShipओवरराइड नहीं किया Vehicle' accelerateऔर इस्तेमाल किया newतो मेरे कोड का तर्क टूट जाएगा। क्या यह नुकसान नहीं है?


77
आप बस उसी तरह से उपयोग करते हैं, जिस तरह से जावा करता है, और बस अपनी शर्तों पर C # कीवर्ड को समझने के लिए समय नहीं लिया है। मैं C # के साथ काम करता हूं, तुरंत शर्तों को समझ गया, और जिस तरह से जावा इसे अजीब करता है उसे ढूंढें।
रॉबर्ट हार्वे

12
IIRC, C # ने "स्पष्टता का पक्ष" किया। यदि आपको स्पष्ट रूप से "नया" या "ओवरराइड" कहना चाहिए, तो यह स्पष्ट रूप से और स्पष्ट रूप से स्पष्ट करता है कि क्या हो रहा है, बजाय इसके कि आप यह जानने की कोशिश करें कि विधि बेस क्लास में कुछ व्यवहार को ओवरराइड कर रही है या नहीं। मुझे यह भी बहुत उपयोगी लगता है कि यह निर्दिष्ट करने में सक्षम हो कि मैं किन तरीकों को आभासी के रूप में निर्दिष्ट करना चाहता हूं, और जो मैं नहीं करता हूं। (जावा ऐसा करता है final, यह इसके ठीक विपरीत है)।
रॉबर्ट हार्वे

15
"जावा में कोई आभासी, नया, ओवरराइड विधि परिभाषा के लिए कीवर्ड हैं।" वहाँ है सिवाय @Override
स्विक


3
@svick एनोटेशन और कीवर्ड एक ही बात नहीं हैं :)
अनिर्बान नाग 'tintinmj'

जवाबों:


86

चूंकि आपने पूछा कि 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()


12
प्रदर्शन बिंदु अब तक पूरी तरह से अप्रचलित है। एक प्रमाण के लिए मेरे बेंचमार्क को दिखाते हैं कि एक गैर-फ़ाइनल के माध्यम से एक क्षेत्र तक पहुंच है लेकिन कभी भी अतिभारित विधि एक चक्र नहीं लेती है।
मातरिनस

7
यकीन है, यह मामला हो सकता है। सवाल यह था कि जब C # ने ऐसा करने का फैसला किया, तो इसे उस समय क्यों किया, और इसलिए यह उत्तर मान्य है। यदि सवाल यह है कि क्या यह अभी भी समझ में आता है, तो यह एक अलग चर्चा है, IMHO।
ओमेर इकबाल

1
मैं आपसे पूरी तरह से सहमत हूं।
माॅर्टिनस

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

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

94

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


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

21
एरिक लिपर्ट ने अपने पोस्ट, वर्चुअल मेथड्स और भंगुर बेस कक्षाओं में इस पर विस्तार से चर्चा की ।
ब्रायन

12
जावा में finalसंशोधक है, तो समस्या क्या है?
सर्ज बोर्स्च

13
@ कोरसी "वस्तुओं को विस्तार के लिए खुला होना चाहिए" - क्यों? यदि आधार वर्ग के डेवलपर ने विरासत के बारे में कभी नहीं सोचा है तो यह लगभग गारंटी है कि कोड में सूक्ष्म निर्भरताएं और धारणाएं हैं जो कीड़े (याद रखें HashMap?) को जन्म देंगी । या तो वंशानुक्रम के लिए डिज़ाइन करें और अनुबंध को स्पष्ट करें या न करें और सुनिश्चित करें कि कोई भी वर्ग विरासत में नहीं ले सकता है। @Overrideएक बैंडेड के रूप में जोड़ा गया था क्योंकि तब तक व्यवहार को बदलने के लिए बहुत देर हो चुकी थी, लेकिन यहां तक ​​कि मूल जावा देवों (विशेष रूप से जोश बलोच) इस बात से सहमत हैं कि यह एक बुरा निर्णय था।
वू

9
@jwenting "जनमत" एक मज़ेदार शब्द है; लोग इसे तथ्यों से जोड़ना पसंद करते हैं ताकि वे उनकी अवहेलना कर सकें। लेकिन जो भी इसके लायक है वह यहोशू बलोच की "राय" (देखें: प्रभावी जावा , आइटम 17 ), जेम्स गोसलिंग की "राय" ( इस साक्षात्कार को देखें ), और जैसा कि ओमर इकबाल के जवाब में कहा गया है , यह एंडर्स जेल्सबर्ग की "राय" भी है। (और हालांकि उन्होंने कभी भी बनाम ऑप्टिंग को चुनने का विरोध नहीं किया, एरिक लिपर्ट स्पष्ट रूप से सहमत हैं कि विरासत भी खतरनाक है।) तो किसका जिक्र कर रहे हैं?
डोभाल

33

जैसा कि रॉबर्ट हार्वे ने कहा, यह वह सब है जिसमें आप अभ्यस्त हैं। मुझे जावा के इस लचीलेपन की कमी अजीब लगती है।

उस ने कहा, यह पहली जगह में क्यों है? इसी कारण से सी # है कि के लिए public, internal(भी "कुछ भी नहीं"), protected, protected internal, और private, लेकिन जावा बस है public, protectedकुछ भी नहीं है, और private। यह आपको ट्रैक करने के लिए अधिक शब्द और कीवर्ड रखने की कीमत पर, आप जो कोडिंग कर रहे हैं, उसके व्यवहार पर बारीक अनाज नियंत्रण प्रदान करता है।

newबनाम के मामले में virtual+override, यह कुछ इस तरह है:

  • यदि आप उपवर्गों को विधि, उपयोग abstractऔर उपवर्ग में लागू करने के लिए बाध्य करना चाहते हैं override
  • यदि आप कार्यक्षमता प्रदान करना चाहते हैं, लेकिन उपवर्ग को प्रतिस्थापित करने, उपयोग करने virtual, और उपवर्ग में अनुमति दें override
  • यदि आप कार्यक्षमता प्रदान करना चाहते हैं जो उपवर्गों को कभी भी ओवरराइड करने की आवश्यकता नहीं होनी चाहिए, तो कुछ भी उपयोग न करें।
    • आप तो एक विशेष मामले के उपवर्ग जो है, तो करता है अलग ढंग से व्यवहार करने की जरूरत है, का उपयोग newउपवर्ग में।
    • यदि आप यह सुनिश्चित करना चाहते हैं कि कोई उपवर्ग कभी भी व्यवहार को ओवरराइड नहीं कर सकता है, sealedतो बेस क्लास में उपयोग करें ।

एक वास्तविक दुनिया उदाहरण के लिए: एक परियोजना मैंने कई अलग-अलग स्रोतों से संसाधित ईकॉमर्स ऑर्डर पर काम किया। एक आधार OrderProcessorथा जिसमें अधिकांश तर्क थे, प्रत्येक स्रोत के बच्चे के वर्ग को ओवरराइड करने के लिए निश्चित abstract/ virtualविधियों के साथ । यह ठीक ऊपर काम किया, जब तक हम एक नया स्रोत है जो एक था मिल गया पूरी तरह से प्रसंस्करण के आदेश के अलग तरीके से, इस तरह के हैं कि हम एक मुख्य कार्य को बदलने के लिए किया था। इस बिंदु पर हमारे पास दो विकल्प थे: 1) virtualआधार विधि में जोड़ें , और overrideबच्चे में; या 2) newबच्चे को जोड़ें ।

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

ध्यान दें है, तथापि, वहाँ है कि है एक व्यवहार इस के साथ जुड़े अंतर ही नहीं, एक अर्थ अंतर। देखें इस लेख जानकारी के लिए। हालाँकि, मैं कभी भी ऐसी स्थिति में नहीं गया जहाँ मुझे इस व्यवहार का लाभ उठाने की आवश्यकता थी।


7
मुझे लगता है कि जानबूझकर newइस तरह से इस्तेमाल करना बग होने की प्रतीक्षा कर रहा है। अगर मैं किसी उदाहरण पर एक विधि कहता हूं, तो मैं यह उम्मीद करता हूं कि मैं हमेशा वही काम करूं, भले ही मैं उदाहरण को उसके आधार वर्ग में डाल दूं। लेकिन ऐसा नहीं है कि newएड के तरीके कैसे व्यवहार करते हैं।
15

@vvick - संभावित रूप से, हाँ। हमारे विशेष परिदृश्य में, ऐसा कभी नहीं होगा, लेकिन इसीलिए मैंने कैविटी को जोड़ा।
बोबसन

newMVC फ्रेमवर्क में WebViewPage <TModel> में एक उत्कृष्ट उपयोग है। लेकिन मुझे भी newघंटों तक एक बग द्वारा फेंक दिया गया है , इसलिए मुझे नहीं लगता कि यह एक अनुचित प्रश्न है।
पीडीआर

1
@ सी। शेम्पेन: किसी भी उपकरण को बुरी तरह से इस्तेमाल किया जा सकता है और यह एक विशेष रूप से तेज उपकरण है - आप खुद को आसानी से काट सकते हैं। लेकिन यह टूलबॉक्स से टूल को हटाने और अधिक प्रतिभाशाली एपीआई डिजाइनर से एक विकल्प को हटाने का एक कारण नहीं है।
पीडीआर

1
@svick सही, यही कारण है कि यह आम तौर पर एक संकलक चेतावनी है। लेकिन "नया" करने की क्षमता होने से किनारे की स्थिति (जैसे दी गई), और इससे भी बेहतर होती है, यह वास्तव में स्पष्ट करता है कि आप अपरिहार्य बग का निदान करने के लिए आने पर कुछ अजीब कर रहे हैं। "क्यों यह वर्ग और केवल यह वर्ग छोटी गाड़ी है ... आह-हह, एक" नया ", चलो परीक्षण करें जहां सुपरक्लास का उपयोग किया जाता है"।
डेवॉर्ड

7

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

यह रनटाइम को सरल करता है, लेकिन कुछ दुर्भाग्यपूर्ण समस्याएं पैदा कर सकता है। माना कि GrafBaseलागू नहीं होता है void DrawParallelogram(int x1,int y1, int x2,int y2, int x3,int y3), लेकिन GrafDerivedइसे एक सार्वजनिक पद्धति के रूप में लागू किया जाता है जो एक समांतर चतुर्भुज खींचता है जिसका गणना किया गया चौथा बिंदु पहले के विपरीत है। आगे मान लीजिए कि एक बाद का संस्करण GrafBaseएक ही हस्ताक्षर के साथ एक सार्वजनिक विधि को लागू करता है, लेकिन इसकी गणना दूसरे के विपरीत चौथे बिंदु है। जो ग्राहक उम्मीद करते हैं, GrafBaseलेकिन एक विचार का संदर्भ प्राप्त करते हैं , वे नई पद्धति के फैशन में आगे के बिंदु की गणना करने की GrafDerivedअपेक्षा करेंगे , लेकिन आधार विधि को बदलने से पहले उपयोग करने वाले ग्राहक व्यवहार की अपेक्षा करेंगे जो मूल रूप से लागू किया गया था।DrawParallelogramGrafBaseGrafDerived.DrawParallelogramGrafDerived

जावा में, वहाँ के लेखक के लिए कोई रास्ता नहीं होगा GrafDerivedग्राहकों है कि नए का उपयोग के साथ उस वर्ग एक समय में होना करने के लिए GrafBase.DrawParallelogramविधि (और अनजान हैं कि हो सकता है GrafDerivedमौजूदा ग्राहक कोड का उपयोग किया जाता के साथ संगतता को तोड़ने के बिना भी मौजूद है) GrafDerived.DrawParallelogramसे पहले GrafBaseयह परिभाषित किया। चूंकि यह DrawParallelogramनहीं बता सकता है कि किस प्रकार का ग्राहक इसे लागू कर रहा है, इसलिए इसे ग्राहक कोड के प्रकारों के द्वारा पहचाना जाना चाहिए। चूंकि दो प्रकार के क्लाइंट कोड में अलग-अलग अपेक्षाएं होती हैं कि यह कैसे व्यवहार करना चाहिए, इसलिए कोई भी तरीका GrafDerivedउनमें से एक (यानी वैध ग्राहक कोड को तोड़ना) की वैध अपेक्षाओं का उल्लंघन करने से बच सकता है।

C # में, यदि GrafDerivedपुन: संकलित नहीं किया गया है , तो रनटाइम यह मान लेगा कि कोड जो DrawParallelogramटाइप के संदर्भों पर विधि को आमंत्रित करता है, GrafDerivedवह व्यवहार की उम्मीद कर रहा होगा GrafDerived.DrawParallelogram()जब यह अंतिम संकलित किया गया था, लेकिन कोड जो टाइप के संदर्भों पर विधि को आमंत्रित करता है, GrafBaseवह अपेक्षा GrafBase.DrawParallelogram(व्यवहार) होगा जोड़ा गया)। यदि GrafDerivedबाद में बढ़ाया की उपस्थिति में पुन: संकलित किया जाता है GrafBase, तो कंपाइलर तब तक स्क्वॉच करेगा जब तक कि प्रोग्रामर या तो यह निर्दिष्ट नहीं कर लेता है कि क्या उसका तरीका विरासत में प्राप्त सदस्य के लिए एक वैध प्रतिस्थापन होना था GrafBase, या क्या उसके व्यवहार को टाइप के संदर्भों में बांधने की आवश्यकता है GrafDerived, लेकिन नहीं प्रकार के संदर्भों के व्यवहार को बदलें GrafBase

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


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

DerivedGraphics? A class using GrafDerived क्या है ?
सी। शेम्पेन

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

1
@ बोबसन: बेहतर है?
सुपरकैट

6

मानक स्थिति:

आप एक बेस क्लास के मालिक हैं जो कई प्रोजेक्ट्स द्वारा उपयोग किया जाता है। आप उक्त आधार वर्ग में बदलाव करना चाहते हैं जो 1 और अनगिनत व्युत्पन्न वर्गों के बीच टूट जाएगा, जो वास्तविक दुनिया मूल्य प्रदान करने वाली परियोजनाओं में हैं (ए फ्रेम एक बार में सबसे अच्छा मूल्य प्रदान करता है, कोई भी वास्तविक मानव एक फ्रेमवर्क नहीं चाहता है, वे चाहते हैं फ्रेमवर्क पर चल रही चीज)। व्युत्पन्न वर्गों के व्यस्त मालिकों को बताते हुए शुभकामनाएँ; "ठीक है, आपको बदलना होगा, आपको" फ्रेमवर्क: प्रोजेक्ट्स के डिलेयर और कॉज ऑफ बग्स "के रूप में निरसन प्राप्त किए बिना उस पद्धति को ओवरराइड नहीं करना चाहिए था"।

विशेष रूप से, इसे गैर-घोषित न करके, आपने स्पष्ट रूप से घोषित किया है कि वे इस कार्य को करने के लिए ठीक थे जो अब आपके परिवर्तन को रोकता है।

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

अंतिम परिणाम: आपका फ्रेमवर्क बेस क्लास कोड अविश्वसनीय रूप से नाजुक और स्थिर हो जाता है, और आप वास्तव में वर्तमान / कुशल रहने के लिए आवश्यक परिवर्तन नहीं कर सकते हैं। वैकल्पिक रूप से, आपके ढांचे को अस्थिरता के लिए एक प्रतिनिधि मिलता है (व्युत्पन्न वर्ग टूटते रहते हैं) और लोग इसका उपयोग बिल्कुल नहीं करेंगे, क्योंकि एक फ्रेमवर्क का उपयोग करने का मुख्य कारण कोडिंग को तेज और अधिक विश्वसनीय बनाना है।

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

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


0

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

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

यह भाषा की परिभाषा का एक मौलिक गुण है। यह सही या गलत नहीं है, यह वही है जो बदल सकता है और बदल नहीं सकता।


2
इससे पहले किए गए 5 अंकों के बारे में कुछ भी बताने की जरूरत नहीं है और पहले ही 5 जवाबों में समझाया गया था
gnat
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.