मेरे व्युत्पन्न वर्ग में एक विधि को कॉल करने को बेस क्लास विधि क्यों कहते हैं?


146

इस कोड पर विचार करें:

class Program
{
    static void Main(string[] args)
    {
        Person person = new Teacher();
        person.ShowInfo();
        Console.ReadLine();
    }
}

public class Person
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public new void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

जब मैं इस कोड को चलाता हूं, तो निम्न आउटपुट किया जाता है:

मैं व्यक्ति हूं

हालाँकि, आप देख सकते हैं कि यह एक उदाहरण है Teacher, का नहीं Person। कोड ऐसा क्यों करता है?


3
एक जावा व्यक्ति से प्रश्न: कंसोल है। रीडलाइन (); इस उदाहरण के लिए आवश्यक है?
रिच

2
@ शिरोज़ मैं आपके प्रश्न का उत्तर नहीं दे सकता - मुझे C # नहीं पता है। मैं एक बहुत ही तुच्छ C # प्रश्न पूछ रहा था, जो यह है कि क्या मुख्य विधि में ReadLine पर कॉल आवश्यक है ताकि व्यक्ति और शिक्षक कक्षाओं में WriteLine को कॉल करने में सक्षम हो।
अमीर

6
जब मुख्य () बाहर निकलता है तो .Net स्वतः कंसोल विंडो को बंद कर देता है। इसके चारों ओर पाने के लिए, हम अतिरिक्त इनपुट की प्रतीक्षा करने के लिए Console.Read () या Console.Readline () का उपयोग करते हैं ताकि कंसोल खुला रहे।
कप्तान केनपाची

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

1
@AakashM धन्यवाद - मैं अपना समय ग्रहण में बिताता हूं जहां कंसोल ग्रहण खिड़की का हिस्सा है, और इसलिए यह बंद नहीं होता है। यह सही समझ में आता है।
रिच

जवाबों:


368

newऔर virtual/ के बीच अंतर है override

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

विधि के कार्यान्वयन का चित्रण

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

1. सार वर्ग

पहला वाला है abstractabstractतरीके बस कहीं नहीं इंगित करते हैं:

अमूर्त वर्गों का चित्रण

यदि आपकी कक्षा में सार सदस्य हैं, तो इसे भी चिह्नित करने की आवश्यकता है abstract, अन्यथा कंपाइलर आपके आवेदन को संकलित नहीं करेगा। आप abstractकक्षाओं के उदाहरण नहीं बना सकते हैं, लेकिन आप उनसे विरासत में प्राप्त कर सकते हैं और अपनी विरासत वाली कक्षाओं के उदाहरण बना सकते हैं और बेस क्लास परिभाषा का उपयोग करके उन्हें एक्सेस कर सकते हैं। आपके उदाहरण में ऐसा दिखेगा:

public abstract class Person
{
    public abstract void ShowInfo();
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

public class Student : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a student!");
    }
}

यदि कहा जाता है, ShowInfoतो कार्यान्वयन के आधार पर, व्यवहार भिन्न होता है:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a student!'

दोनों, Students और Teachers s हैं Person, लेकिन वे अलग व्यवहार करते हैं जब उन्हें अपने बारे में तुरंत जानकारी देने के लिए कहा जाता है। हालांकि, उन्हें अपनी जानकारी को संकेत देने के लिए पूछने का तरीका समान है: Personवर्ग इंटरफ़ेस का उपयोग करना ।

तो पर्दे के पीछे क्या होता है, जब आपको विरासत मिलती है Person? लागू ShowInfoकरते समय, पॉइंटर कहीं और इंगित नहीं कर रहा है, यह अब वास्तविक कार्यान्वयन की ओर इशारा करता है ! जब एक बनाने Studentउदाहरण है, यह की ओर इशारा करता Studentरों ShowInfo:

विरासत में मिली विधियों का चित्रण

2. आभासी तरीके

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

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am a person!");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am a teacher!");
    }
}

मुख्य अंतर यह है कि, आधार सदस्य कहीं भी Person.ShowInfoइंगित नहीं कर रहा है। यह भी कारण है, आप क्यों Person(और इस तरह इसे abstractअब चिह्नित किए जाने की आवश्यकता नहीं है) के उदाहरण बना सकते हैं :

बेस क्लास के अंदर एक आभासी सदस्य का चित्रण

आपको ध्यान देना चाहिए, कि यह अब के लिए पहली छवि से अलग नहीं दिखता है। ऐसा इसलिए है क्योंकि virtualविधि " मानक तरीके " को लागू करने की ओर इशारा कर रही है । का उपयोग करते हुए virtual, आप बता सकते हैं Persons, कि वे कर सकते हैं (नहीं चाहिए ) के लिए एक अलग कार्यान्वयन प्रदान ShowInfo। यदि आप एक अलग कार्यान्वयन (उपयोग करते हुए override) प्रदान करते हैं , जैसा कि मैंने Teacherऊपर के लिए किया था , तो छवि उसी के लिए दिखाई देगी abstract। कल्पना कीजिए, हमने Students के लिए एक कस्टम कार्यान्वयन प्रदान नहीं किया :

public class Student : Person
{
}

कोड को इस तरह कहा जाएगा:

Person person = new Teacher();
person.ShowInfo();    // Shows 'I am a teacher!'

person = new Student();
person.ShowInfo();    // Shows 'I am a person!'

और Studentइस तरह की छवि दिखाई देगी:

वर्चुअल-कीवर्ड का उपयोग करके एक विधि के डिफ़ॉल्ट कार्यान्वयन का चित्रण

3. जादू `new` कीवर्ड उर्फ" शैडोइंग "

newइस के आसपास एक हैक अधिक है। आप सामान्यीकृत कक्षाओं में तरीके प्रदान कर सकते हैं, जिनका आधार वर्ग / इंटरफ़ेस में विधियों के समान नाम हैं। दोनों अपने स्वयं के, कस्टम कार्यान्वयन की ओर इशारा करते हैं:

नए-कीवर्ड का उपयोग करके "चारों ओर" का चित्रण

कार्यान्वयन एक जैसा दिखता है, आपने प्रदान किया। आपके द्वारा विधि तक पहुँचने के तरीके के आधार पर व्यवहार भिन्न होता है:

Teacher teacher = new Teacher();
Person person = (Person)teacher;

teacher.ShowInfo();    // Prints 'I am a teacher!'
person.ShowInfo();     // Prints 'I am a person!'

यह व्यवहार चाहा जा सकता है, लेकिन आपके मामले में यह भ्रामक है।

मुझे आशा है कि यह चीजें आपके लिए समझने के लिए स्पष्ट बनाती हैं!


9
आपके शानदार उत्तर के लिए धन्यवाद

6
उन आरेखों को उत्पन्न करने के लिए आपने क्या उपयोग किया?
ब्लूराजा - डैनी पफ्लुगुएफ्ट

2
उत्कृष्ट और बहुत गहन उत्तर।
निक बुगालिस

8
tl; dr आपने उपयोग किया newजो फंक्शन की विरासत को तोड़ता है और नए फंक्शन को सुपरक्लास के फंक्शन से अलग बनाता है
शाफ़्ट फ्रीक

3
@ टायमोन: वास्तव में नहीं ... मैं सिर्फ यह स्पष्ट करना चाहता था कि कॉल अब खिलाफ हो जाता है Person, नहीं Student;)
कार्स्टन

45

C # में उप-प्रकार बहुरूपता स्पष्ट आभासीता का उपयोग करता है, C ++ के समान लेकिन जावा के विपरीत। इसका मतलब है कि आपको स्पष्ट रूप से तरीकों को ओवररिडेबल (यानी virtual) के रूप में चिह्नित करना होगा । C # में आपको overrideटाइपो को रोकने के लिए ओवरराइडिंग (यानी ) के रूप में स्पष्ट रूप से ओवरराइडिंग विधियों को चिह्नित करना होगा।

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

आपके प्रश्न के कोड में, आप उपयोग करते हैं new, जो ओवरराइड करने के बजाय छायांकन करता है । शैडोइंग केवल रनटाइम शब्दार्थ के बजाय संकलन-समय शब्दार्थ को प्रभावित करता है, इसलिए अनपेक्षित आउटपुट।


4
ओपी को कौन कहता है कि उन्हें क्या पता है।
कोल जॉन्सन

@ColeJohnson मैं एक स्पष्टीकरण जोड़ूंगा।

25

आपको विधि को आभासी बनाना होगा और आपको पैरेंट क्लास के संदर्भ में रखी गई क्लास ऑब्जेक्ट की विधि को कॉल करने के लिए चाइल्ड क्लास में फ़ंक्शन को ओवरराइड करना होगा।

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}
public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

आभासी तरीके

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

छायांकन के लिए नए का उपयोग करना

आप ओवरराइड के बजाय नए कुंजी शब्द का उपयोग कर रहे हैं, यह वही है जो नया करता है

  • यदि व्युत्पन्न वर्ग में विधि नए या ओवरराइड कीवर्ड से पहले नहीं है, तो कंपाइलर एक चेतावनी जारी करेगा और विधि ऐसा व्यवहार करेगी जैसे कि नया कीवर्ड मौजूद था।

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

जल्दी बाध्यकारी वी.एस. लेट बाइंडिंग

हम सामान्य विधि (आभासी नहीं) के लिए संकलित समय पर बाध्यकारी हैं जो कि क्यूरेट केस है जो कंपाइलर बेस क्लास की विधि के लिए कॉल को बाध्य करेगा जो ऑब्जेक्ट के बजाय संदर्भ प्रकार (बेस क्लास) की विधि है जो आधार के रेफरी में आयोजित होता है वर्ग अर्थात व्युत्पन्न वर्ग वस्तु । ऐसा इसलिए है क्योंकि यह ShowInfoवर्चुअल तरीका नहीं है। वर्चुअल मेथड टेबल (वाइबेट) का उपयोग करते हुए (वर्चुअल / ओवरराइड विधि) रनटाइम पर लेट बाइंडिंग की जाती है ।

एक सामान्य फ़ंक्शन के लिए, कंपाइलर मेमोरी में इसके संख्यात्मक स्थान का पता लगा सकता है। तब यह जब फ़ंक्शन कहा जाता है, तो यह इस पते पर फ़ंक्शन को कॉल करने के लिए एक निर्देश उत्पन्न कर सकता है।

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


7

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

C # में, एक अन्य उत्तर के रूप में, पारंपरिक तरीके से ओवरराइडिंग स्पष्ट होनी चाहिए; बेस क्लास पद्धति को चिन्हित किया जाना चाहिए virtualऔर व्युत्पन्न वर्ग को विशेष रूप overrideसे बेस क्लास विधि चाहिए। यदि ऐसा किया जाता है, तो इससे कोई फर्क नहीं पड़ता कि क्या वस्तु को आधार वर्ग या व्युत्पन्न वर्ग का उदाहरण माना जाता है; व्युत्पन्न विधि पाया और कहा जाता है। यह उसी तरह से किया जाता है जैसे सी ++ में; "वर्चुअल" या "ओवरराइड" चिह्नित एक विधि, जब संकलित की जाती है, तो संदर्भित ऑब्जेक्ट के वास्तविक प्रकार का निर्धारण करके "लेट" (रनटाइम पर) हल की जाती है, और चर प्रकार से पेड़ के साथ नीचे की ओर वस्तु पदानुक्रम को वास्तविक ऑब्जेक्ट प्रकार से ट्रेस किया जाता है, चर प्रकार द्वारा परिभाषित विधि का सबसे व्युत्पन्न कार्यान्वयन खोजने के लिए।

यह जावा से भिन्न है, जो "अंतर्निहित ओवरराइड्स" की अनुमति देता है; उदाहरण के तरीकों (गैर-स्थैतिक) के लिए, बस एक ही हस्ताक्षर (नाम और संख्या / मापदंडों के प्रकार) की एक विधि को परिभाषित करने से उपवर्ग सुपरक्लास को ओवरराइड करने का कारण होगा।

चूँकि अक्सर गैर-वर्चुअल विधि जिसे आप नियंत्रित नहीं करते हैं, की कार्यक्षमता को बढ़ाने या ओवरराइड करने के लिए उपयोगी है, C # में newप्रासंगिक कीवर्ड भी शामिल है । newकीवर्ड "खाल" जनक बजाय इसे अधिभावी की विधि। किसी भी अंतर्निहित विधि को छिपाया जा सकता है चाहे वह आभासी हो या न हो; यह आपको, डेवलपर को, उन सदस्यों का लाभ उठाने की अनुमति देता है, जिन्हें आप माता-पिता से विरासत में प्राप्त करना चाहते हैं, जो आपके पास नहीं हैं, जबकि आप अभी भी अपने कोड के उपभोक्ताओं को समान "इंटरफ़ेस" पेश करने की अनुमति नहीं देते हैं।

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

इसके अलावा, न केवल newकीवर्ड इसे स्पष्ट रूप से करेगा, C # निहित पद्धति को छिपाने की अनुमति देता है; बस एक ही हस्ताक्षर के साथ एक विधि को मूल वर्ग विधि के रूप में परिभाषित किए बिना, overrideया new, इसे छिपाएंगे (हालांकि यह एक संकलक चेतावनी या ReSharper या CodeRush जैसे कुछ रिफैक्टरिंग सहायकों से शिकायत का उत्पादन करेगा)। यह समझौता है C # के डिजाइनर C ++ के स्पष्ट ओवरराइड बनाम जावा के निहितार्थों के बीच आए, और जब यह सुरुचिपूर्ण है, तो यह हमेशा उस व्यवहार का उत्पादन नहीं करता है जिसकी आप उम्मीद करते हैं यदि आप पुरानी भाषाओं में से किसी एक पृष्ठभूमि से आते हैं।

यहां नया सामान दिया गया है: जब आप दो कीवर्ड को एक लंबी विरासत श्रृंखला में जोड़ते हैं तो यह जटिल हो जाता है। निम्नलिखित को धयान मे रखते हुए:

class Foo { public virtual void DoFoo() { Console.WriteLine("Foo"); } }
class Bar:Foo { public override sealed void DoFoo() { Console.WriteLine("Bar"); } }
class Baz:Bar { public virtual void DoFoo() { Console.WriteLine("Baz"); } }
class Bai:Baz { public override void DoFoo() { Console.WriteLine("Bai"); } }
class Bat:Bai { public new void DoFoo() { Console.WriteLine("Bat"); } }
class Bak:Bat { }

Foo foo = new Foo();
Bar bar = new Bar();
Baz baz = new Baz();
Bai bai = new Bai();
Bat bat = new Bat();

foo.DoFoo();
bar.DoFoo();
baz.DoFoo();
bai.DoFoo();
bat.DoFoo();

Console.WriteLine("---");

Foo foo2 = bar;
Bar bar2 = baz;
Baz baz2 = bai;
Bai bai2 = bat;
Bat bat2 = new Bak();

foo2.DoFoo();
bar2.DoFoo();
baz2.DoFoo();
bai2.DoFoo();    

Console.WriteLine("---");

Foo foo3 = bak;
Bar bar3 = bak;
Baz baz3 = bak;
Bai bai3 = bak;
Bat bat3 = bak;

foo3.DoFoo();
bar3.DoFoo();
baz3.DoFoo();
bai3.DoFoo();    
bat3.DoFoo();

आउटपुट:

Foo
Bar
Baz
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat
---
Bar
Bar
Bai
Bai
Bat

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

पांच का दूसरा सेट प्रत्येक उदाहरण को तत्काल मूल प्रकार के एक चर में निर्दिष्ट करने का परिणाम है। अब, व्यवहार में कुछ अंतर सामने आते हैं; foo2, जो वास्तव में एक Barकलाकार के रूप में एक कास्ट है Foo, अभी भी वास्तविक ऑब्जेक्ट प्रकार बार की अधिक व्युत्पन्न विधि ढूंढेगा। bar2एक है Baz, लेकिन इसके विपरीत है foo2, क्योंकि बाज स्पष्ट रूप से बार के कार्यान्वयन को ओवरराइड नहीं करता है (यह नहीं कर सकता है, sealedइसे बार नहीं), यह रनटाइम द्वारा "टॉप-डाउन" देखते समय नहीं देखा जाता है, इसलिए बार के कार्यान्वयन को इसके बजाय कहा जाता है। ध्यान दें कि बाज को newकीवर्ड का उपयोग करने की आवश्यकता नहीं है ; यदि आप कीवर्ड को छोड़ देते हैं तो आपको एक कंपाइलर चेतावनी मिल जाएगी, लेकिन C # में निहित व्यवहार मूल विधि को छिपाने के लिए है। baz2एक है Bai, जो ओवरराइड Bazकीnewकार्यान्वयन, इसलिए इसका व्यवहार समान है foo2; बाई में वास्तविक वस्तु प्रकार के कार्यान्वयन को कहा जाता है। bai2एक है Bat, जो फिर से अपने माता-पिता Baiकी विधि कार्यान्वयन को छुपाता है , और यह उसी तरह का व्यवहार करता है, bar2भले ही बाई के कार्यान्वयन को सील नहीं किया गया है, इसलिए सैद्धांतिक रूप से बैट को छिपी विधि के बजाय ओवरराइड किया जा सकता है। अंत में, bat2एक है Bak, जिसमें किसी भी प्रकार का ओवरराइड कार्यान्वयन नहीं है, और बस अपने माता-पिता का उपयोग करता है।

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


4

कृपया C # में बहुरूपता के बारे में पढ़ें: बहुरूपता (C # प्रोग्रामिंग गाइड)

यह वहां से एक उदाहरण है:

जब नए कीवर्ड का उपयोग किया जाता है, तो बदले गए बेस क्लास सदस्यों के बजाय नए क्लास सदस्यों को बुलाया जाता है। उन आधार वर्ग के सदस्यों को छिपे हुए सदस्य कहा जाता है। छिपे वर्ग के सदस्यों को तब भी बुलाया जा सकता है यदि व्युत्पन्न वर्ग का एक उदाहरण बेस क्लास के एक उदाहरण के लिए डाला जाता है। उदाहरण के लिए:

DerivedClass B = new DerivedClass();
B.DoWork();  // Calls the new method.

BaseClass A = (BaseClass)B;
A.DoWork();  // Calls the old method.

3

आपको इसे बनाने की आवश्यकता है virtualऔर फिर उस फ़ंक्शन को ओवरराइड करें Teacher। जैसा कि आप एक व्युत्पन्न वर्ग को संदर्भित करने के लिए बेस पॉइंटर का उपयोग और विरासत कर रहे हैं, आपको इसका उपयोग करके ओवरराइड करने की आवश्यकता है virtual। एक व्युत्पन्न वर्ग संदर्भ पर वर्ग विधि को newछिपाने के लिए है baseऔर वर्ग संदर्भ नहीं है base


3

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

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

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.foo();        // A.foo()
            a.foo2();       // A.foo2()

            a = new B();    
            a.foo();        // B.foo()
            a.foo2();       // A.foo2()
            //a.novel() is not available here

            a = new C();
            a.foo();        // C.foo()
            a.foo2();       // A.foo2()

            B b1 = (B)a;    
            b1.foo();       // C.foo()
            b1.foo2();      // B.foo2()
            b1.novel();     // B.novel()

            Console.ReadLine();
        }
    }


    class A
    {
        public virtual void foo()
        {
            Console.WriteLine("A.foo()");
        }

        public void foo2()
        {
            Console.WriteLine("A.foo2()");
        }
    }

    class B : A
    {
        public override void foo()
        {
            // This is an override
            Console.WriteLine("B.foo()");
        }

        public new void foo2()      // Using the 'new' keyword doesn't make a difference
        {
            Console.WriteLine("B.foo2()");
        }

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

    class C : B
    {
        public override void foo()
        {
            Console.WriteLine("C.foo()");
        }

        public new void foo2()
        {
            Console.WriteLine("C.foo2()");
        }
    }
}

कोड की निम्नलिखित पंक्ति के लिए एक और छोटी विसंगति है:

A a = new B();    
a.foo(); 

वी। एस। कंपाइलर (इन्टेलिसेन्स) ए.फू () के रूप में दिखाएगा।

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

इस कोड नमूने को इन कैविनेट को सीमांकित करने में मदद करनी चाहिए!


2

C # पैरेंट / चाइल्ड क्लास ओवरराइड व्यवहार में जावा से अलग है। जावा में डिफ़ॉल्ट रूप से सभी विधियां आभासी हैं, इसलिए जो व्यवहार आप चाहते हैं वह बॉक्स से बाहर समर्थित है।

C # में आपको बेस क्लास में वर्चुअल के रूप में एक विधि को चिह्नित करना होगा, फिर आपको वही मिलेगा जो आप चाहते हैं।


2

नए कीवर्ड बताते हैं कि वर्तमान कक्षा में विधि केवल काम करता है, तो आप प्रकार शिक्षक के एक चर में संग्रहीत वर्ग शिक्षक का एक उदाहरण है। या आप इसे कास्टिंग का उपयोग करके ट्रिगर कर सकते हैं: ((शिक्षक) व्यक्ति) .ShowInfo ()


1

यहाँ The शिक्षक ’का typeof(Person)प्रकार है और यह प्रकार शिक्षक वर्ग के बारे में कुछ भी नहीं जानता है और व्युत्पन्न प्रकारों में किसी भी तरीके की तलाश करने की कोशिश नहीं करता है। शिक्षक वर्ग की विधि कॉल करने के लिए आप अपने चर डाली चाहिए: (person as Teacher).ShowInfo()

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

public class Program
{
    private static void Main(string[] args)
    {
        Person teacher = new Teacher();
        teacher.ShowInfo();

        Person incognito = new IncognitoPerson ();
        incognito.ShowInfo();

        Console.ReadLine();
    }
}

public class Person
{
    public virtual void ShowInfo()
    {
        Console.WriteLine("I am Person");
    }
}

public class Teacher : Person
{
    public override void ShowInfo()
    {
        Console.WriteLine("I am Teacher");
    }
}

public class IncognitoPerson : Person
{

}

1

बहुत देर हो सकती है ... लेकिन सवाल सरल है और जवाब में जटिलता का स्तर समान होना चाहिए।

आपके कोड में परिवर्तनशील व्यक्ति शिक्षक के बारे में कुछ नहीं जानता है। बेस क्लास संदर्भ से अंतिम विधि को कॉल करने का कोई तरीका नहीं है, क्योंकि यह आभासी नहीं है।

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


0

संकलक ऐसा करता है क्योंकि यह नहीं जानता कि यह एक है Teacher। यह सब जानता है कि यह एक Personया उससे प्राप्त कुछ है। तो यह सब कर सकते हैं Person.ShowInfo()विधि कहते हैं ।


0

बस एक संक्षिप्त जवाब देना चाहता था -

आपको उपयोग करना चाहिए virtualऔर ऐसी overrideकक्षाओं में जिन्हें ओवरराइड किया जा सकता है। virtualउन विधियों के लिए उपयोग करें जिन्हें बच्चे की कक्षाओं द्वारा ओवरराइड किया जा सकता है और उन overrideतरीकों के लिए उपयोग करना चाहिए जो ऐसे virtualतरीकों को ओवरराइड करना चाहिए ।


0

मैंने उसी कोड को लिखा था जैसा कि यू ने कुछ बदलावों को छोड़कर जावा में उल्लेख किया है और इसे छोड़कर ठीक काम किया है। बेस क्लास की विधि ओवरराइड है और इसलिए प्रदर्शित आउटपुट "मैं शिक्षक हूं"।

कारण: जैसा कि हम बेस क्लास (जो व्युत्पन्न वर्ग के उदाहरण का उल्लेख करने में सक्षम है) का एक संदर्भ बना रहे हैं, जो वास्तव में व्युत्पन्न वर्ग के संदर्भ से युक्त है। और जैसा कि हम जानते हैं कि उदाहरण हमेशा अपने तरीकों को देखता है, अगर यह पाता है कि यह इसे निष्पादित करता है, और अगर यह परिभाषा नहीं मिलती है तो यह पदानुक्रम में ऊपर जाता है।

public class inheritance{

    public static void main(String[] args){

        Person person = new Teacher();
        person.ShowInfo();
    }
}

class Person{

    public void ShowInfo(){
        System.out.println("I am Person");
    }
}

class Teacher extends Person{

    public void ShowInfo(){
        System.out.println("I am Teacher");
    }
}

0

कीथ एस के उत्कृष्ट प्रदर्शन और हर एक की गुणवत्ता के जवाब के लिए और uber पूर्णता के लिए बिल्डिंग आगे बढ़ने देती है और स्पष्ट इंटरफ़ेस कार्यान्वयन को टॉस करती है कि वह कैसे काम करता है। नीचे पर विचार करें:

नामस्थान LinqConsoleApp {

class Program
{

    static void Main(string[] args)
    {


        Person person = new Teacher();
        Console.Write(GetMemberName(() => person) + ": ");
        person.ShowInfo();

        Teacher teacher = new Teacher();
        Console.Write(GetMemberName(() => teacher) + ": ");
        teacher.ShowInfo();

        IPerson person1 = new Teacher();
        Console.Write(GetMemberName(() => person1) + ": ");
        person1.ShowInfo();

        IPerson person2 = (IPerson)teacher;
        Console.Write(GetMemberName(() => person2) + ": ");
        person2.ShowInfo();

        Teacher teacher1 = (Teacher)person1;
        Console.Write(GetMemberName(() => teacher1) + ": ");
        teacher1.ShowInfo();

        Person person4 = new Person();
        Console.Write(GetMemberName(() => person4) + ": ");
        person4.ShowInfo();

        IPerson person3 = new Person();
        Console.Write(GetMemberName(() => person3) + ": ");
        person3.ShowInfo();

        Console.WriteLine();

        Console.ReadLine();

    }

    private static string GetMemberName<T>(Expression<Func<T>> memberExpression)
    {
        MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
        return expressionBody.Member.Name;
    }

}
interface IPerson
{
    void ShowInfo();
}
public class Person : IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Person == " + this.GetType());
    }
    void IPerson.ShowInfo()
    {
        Console.WriteLine("I am interface Person == " + this.GetType());
    }
}
public class Teacher : Person, IPerson
{
    public void ShowInfo()
    {
        Console.WriteLine("I am Teacher == " + this.GetType());
    }
}

}

यहाँ उत्पादन है:

व्यक्ति: मैं व्यक्ति हूं == LinqConsoleApp.Teacher

शिक्षक: मैं शिक्षक == LinqConsoleApp.Teacher हूं

person1: मैं शिक्षक हूं == LinqConsoleApp.Teacher

person2: मैं शिक्षक हूं == LinqConsoleApp.Teacher

शिक्षक 1: मैं शिक्षक हूं == LinqConsoleApp.Teacher

person4: मैं व्यक्ति हूं == LinqConsoleApp.Person

person3: मैं इंटरफ़ेस पर्सन हूं == LinqConsoleApp.Person

ध्यान देने योग्य दो बातें:
The Teacher.ShowInfo () विधि नए कीवर्ड को छोड़ देती है। जब नया छोड़ा जाता है तो विधि व्यवहार वैसा ही होता है जैसे कि नया कीवर्ड स्पष्ट रूप से परिभाषित किया गया था।

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

व्यक्ति को ShowInfo का आधार कार्यान्वयन मिलता है क्योंकि शिक्षक वर्ग आधार कार्यान्वयन (कोई वर्चुअल घोषणा नहीं) को ओवरराइड नहीं कर सकता है और व्यक्ति है ।GetType (शिक्षक) इसलिए यह शिक्षक वर्ग के कार्यान्वयन को छुपाता है।

शिक्षक को ShowInfo का व्युत्पन्न शिक्षक कार्यान्वयन मिलता है क्योंकि शिक्षक क्योंकि यह टाइपोफ (शिक्षक) है और यह व्यक्ति के उत्तराधिकार स्तर पर नहीं है।

person1 को व्युत्पन्न शिक्षक कार्यान्वयन मिलता है क्योंकि यह है ।GetType (शिक्षक) और अंतर्निहित नया कीवर्ड आधार कार्यान्वयन को छुपाता है।

person2 भी व्युत्पन्न शिक्षक कार्यान्वयन प्राप्त करता है, भले ही यह IPerson को लागू करता है और इसे IPerson को एक स्पष्ट कलाकार मिलता है। यह फिर से है क्योंकि शिक्षक वर्ग IPerson.ShowInfo () पद्धति को स्पष्ट रूप से लागू नहीं करता है।

शिक्षक 1 भी व्युत्पन्न शिक्षक कार्यान्वयन प्राप्त करता है क्योंकि यह है। गेट टाइप (शिक्षक)।

केवल व्यक्ति 3 को शोइन्फो का IPerson कार्यान्वयन प्राप्त होता है क्योंकि केवल व्यक्ति वर्ग स्पष्ट रूप से विधि लागू करता है और person3 IPerson प्रकार का एक उदाहरण है।

एक इंटरफ़ेस को स्पष्ट रूप से लागू करने के लिए आपको लक्ष्य इंटरफ़ेस प्रकार के एक संस्करण का उदाहरण देना होगा और एक वर्ग को स्पष्ट रूप से इंटरफ़ेस सदस्य (सदस्यों) को पूरी तरह से लागू करना होगा।

सूचना 4 भी व्यक्ति को IPerson.ShowInfo कार्यान्वयन नहीं मिलता है। ऐसा इसलिए है क्योंकि person4 है भले ही .GetType (व्यक्ति) और भले ही व्यक्ति IPerson को लागू करता है, person4 IPerson का उदाहरण नहीं है।


मुझे लगता है कि कोड को ठीक से चुनौती देना थोड़ा मुश्किल है। अभी इसे सुंदर करने का समय नहीं ...
फौलादी

0

LinQPad नमूना नेत्रहीन लॉन्च करने और कोड के दोहराव को कम करने के लिए जो मुझे लगता है कि आप क्या करने की कोशिश कर रहे थे।

void Main()
{
    IEngineAction Test1 = new Test1Action();
    IEngineAction Test2 = new Test2Action();
    Test1.Execute("Test1");
    Test2.Execute("Test2");
}

public interface IEngineAction
{
    void Execute(string Parameter);
}

public abstract class EngineAction : IEngineAction
{
    protected abstract void PerformAction();
    protected string ForChildren;
    public void Execute(string Parameter)
    {  // Pretend this method encapsulates a 
       // lot of code you don't want to duplicate 
      ForChildren = Parameter;
      PerformAction();
    }
}

public class Test1Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Performed: " + ForChildren).Dump();
    }
}

public class Test2Action : EngineAction
{
    protected override void PerformAction()
    {
        ("Actioned: " + ForChildren).Dump();
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.