आधार कॉल कैसे करें ।base.method ()?


126
// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}

परिणाम है:

विशेष व्युत्पन्न से कहा जाता है।
व्युत्पन्न से बुलाया। / * यह अपेक्षित नहीं है * /
आधार से ।

मैं विशेष श्रेणी को फिर से कैसे लिख सकता हूं ताकि मध्यम वर्ग "व्युत्पन्न" की विधि को न कहा जाए?

अद्यतन: मैं आधार के बजाय व्युत्पन्न से प्राप्त करना चाहता हूं, इसका कारण यह है कि व्युत्पन्न वर्ग में बहुत सारे अन्य कार्यान्वयन हैं। जब से मैं base.base.method()यहाँ नहीं कर सकता , मुझे लगता है कि निम्नलिखित करने का सबसे अच्छा तरीका है?

// सोर्स कोड नहीं बदल सकता

class Derived : Base
{
    public override void Say()
    {
        CustomSay();

        base.Say();
    }

    protected virtual void CustomSay()
    {
        Console.WriteLine("Called from Derived.");
    }
}

class SpecialDerived : Derived
{
    /*
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
    */

    protected override void CustomSay()
    {
        Console.WriteLine("Called from Special Derived.");
    }
}

अपने अपडेट से मिलान करने के लिए संपादन।
जोशजोर्डन

जवाबों:


106

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

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        var ptr = typeof(Base).GetMethod("Say").MethodHandle.GetFunctionPointer();            
        var baseSay = (Action)Activator.CreateInstance(typeof(Action), this, ptr);
        baseSay();            
    }
}

46
मैं वास्तव में इस उत्तर को पसंद करता हूं क्योंकि यह प्रश्न अनुशंसित नहीं था या नहीं या यह एक अच्छा विचार है या नहीं, सवाल यह था कि क्या ऐसा करने का कोई तरीका है या नहीं और यदि ऐसा है तो क्या तरीका है।
शाविस 14

3
विशेष रूप से तब उपयोगी होता है जब आपको चौखटे / देयता से निबटना होता है जिसमें अत्यधिकता की कमी होती है। उदाहरण के लिए, मुझे NHibernate के लिए ऐसे समाधानों का सहारा लेने की कभी जरूरत नहीं पड़ी, जो कि अत्यधिक व्यापक है। लेकिन Asp.Net आइडेंटिटी, Entity फ्रेमवर्क, Asp.Net Mvc से निपटने के लिए, मैं नियमित रूप से अपनी जरूरतों के लिए अनुपलब्ध सुविधाओं या हार्ड कोडित व्यवहारों को संभालने के लिए इस तरह के हैक का उपयोग कर समाप्त करता हूं।
फ्रेडरिक

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

1
क्या होगा यदि हमें अंतर्निहित फ़ंक्शन से वापसी मूल्य की आवश्यकता है?
पर्किन्स

2
कोई बात नहीं, यह पता लगा। Func<stuff>इसके बजाय कास्टAction
पर्किन्स

92

यह एक बुरा प्रोग्रामिंग अभ्यास है, और C # में अनुमति नहीं है। यह एक बुरा प्रोग्रामिंग अभ्यास है क्योंकि

  • ग्रैंडबेस के विवरण आधार का कार्यान्वयन विवरण हैं; आपको उन पर भरोसा नहीं करना चाहिए। बेस क्लास ग्रैंडबेस के एब्स्ट्रैक्शन ओवरटॉप प्रदान कर रहा है; आपको उस अमूर्त का उपयोग करना चाहिए, इससे बचने के लिए बाईपास का निर्माण नहीं करना चाहिए।

  • पिछले बिंदु के एक विशिष्ट उदाहरण को स्पष्ट करने के लिए: यदि अनुमति दी जाती है, तो यह पैटर्न कोड को भंगुर-आधार-वर्ग की विफलताओं के लिए अतिसंवेदनशील बनाने का एक और तरीका होगा। मान लीजिए Cसे व्युत्पन्न Bहै जहाँ से व्युत्पन्न A। की एक विधि को कॉल करने के लिए Cउपयोग में कोड । तब लेखक को पता चलता है कि उन्होंने कक्षा में बहुत अधिक गियर डाल दिए हैं , और एक बेहतर तरीका यह है कि मध्यवर्ती वर्ग को बनाया जाए जो इससे उत्पन्न होता है , और इससे प्राप्त होता है । उस परिवर्तन के बाद, में कोड एक विधि को बुला रहा है , अंदर नहीं , क्योंकिbase.baseABBB2ABB2CB2AC लेखक ने एक धारणा बनाई है कि कार्यान्वयन विवरण B, अर्थात् इसका सीधा आधार वर्ग हैA, और कभी नहीं बदलेगा। C # में कई डिजाइन निर्णय विभिन्न प्रकार की भंगुर आधार विफलताओं की संभावना को कम करने के लिए हैं; base.baseगैरकानूनी बनाने का निर्णय पूरी तरह से उस विफलता पैटर्न के इस विशेष स्वाद को रोकता है।

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

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


4
@ जादौन: तब रचना को विरासत में पसंद करते हैं। आपका वर्ग या तो बेस या ग्रैंडबेस का एक उदाहरण ले सकता है, और वर्ग फिर आवृत्ति को कार्यक्षमता को सुरक्षित कर सकता है।
एरिक लिपर्ट

4
@BlackOverlord: जब से आप इस विषय पर दृढ़ता से महसूस करते हैं, तो सात साल पुराने इस सवाल का जवाब खुद क्यों नहीं लिखते? इस तरह हम सभी को इस विषय पर आपकी बुद्धिमत्ता से लाभ मिलता है, और तब आपने StackOverflow पर दो उत्तर लिखे होंगे, जो आपके कुल योगदान को दोगुना करेंगे। यह एक जीत है।
एरिक लिपर्ट

4
@ एरिक लिपर्ट: दो कारण हैं कि मैंने अपना जवाब क्यों नहीं लिखा: पहला, मुझे नहीं पता था कि यह कैसे करना है, इसलिए मुझे यह विषय मिला। दूसरा, इस पेज के नीचे ईवैक का एक व्यापक उत्तर है।
ब्लैकऑवरलॉर्ड

3
@ डनव: मुझे जवाब पता है; यह मेरे उत्तर का पहला वाक्य है: C # में वांछित सुविधा की अनुमति नहीं है क्योंकि यह एक बुरा प्रोग्रामिंग अभ्यास है । आप इसे C # में कैसे करते हैं? तुम नहीं। यह धारणा कि मुझे इस प्रश्न का उत्तर नहीं पता है कि यह एक मनोरंजक है, लेकिन हम इसे बिना किसी टिप्पणी के पास कर देंगे। अब, यदि आपको यह उत्तर असंतोषजनक लगता है, तो अपना स्वयं का उत्तर क्यों न लिखें जो आपको लगता है कि बेहतर काम करता है? इस तरह हम सभी आपकी बुद्धि और अनुभव से सीखते हैं, और आप इस वर्ष आपके द्वारा पोस्ट किए गए उत्तरों की संख्या को भी दोगुना कर देंगे।
एरिक लिपर्ट

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

22

आप C # से नहीं कर सकते। IL से, यह वास्तव में समर्थित है। आप अपने किसी भी मूल वर्ग के लिए एक गैर-पुण्य कॉल कर सकते हैं ... लेकिन कृपया नहीं। :)


11

उत्तर (जो मुझे पता है कि आप जो खोज रहे हैं वह नहीं है):

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

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

संपादित करें:

आपका संपादन कार्य करता है, लेकिन मुझे लगता है कि मैं कुछ इस तरह का उपयोग करूंगा:

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

बेशक, एक वास्तविक कार्यान्वयन में, आप एक्स्टेंसिबिलिटी और मेंटेनेंस के लिए ऐसा कुछ और कर सकते हैं:

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

फिर, व्युत्पन्न वर्ग अपने माता-पिता की स्थिति को उचित रूप से नियंत्रित कर सकते हैं।


3
क्यों न केवल Derivedकॉल में एक संरक्षित फ़ंक्शन लिखें Base.Say, ताकि इसे कहा जा सके SpecialDerived? सरल, नहीं?
नवफाल

7

बस बच्चे को एक विशिष्ट माता-पिता वर्ग में क्यों नहीं डाला जाता है और फिर विशिष्ट कार्यान्वयन को लागू करता है? यह एक विशेष मामला स्थिति है और एक विशेष मामले के समाधान का उपयोग किया जाना चाहिए। आपको newहालांकि बच्चों के तरीकों में कीवर्ड का उपयोग करना होगा ।

public class SuperBase
{
    public string Speak() { return "Blah in SuperBase"; }
}

public class Base : SuperBase
{
    public new string Speak() { return "Blah in Base"; }
}

public class Child : Base
{
    public new string Speak() { return "Blah in Child"; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Child childObj = new Child();

        Console.WriteLine(childObj.Speak());

        // casting the child to parent first and then calling Speak()
        Console.WriteLine((childObj as Base).Speak()); 

        Console.WriteLine((childObj as SuperBase).Speak());
    }
}

2
यदि आपके पास एक इंजन है जो आधार या बच्चे के बारे में नहीं जान सकता है, और उस इंजन द्वारा कॉल किए जाने पर सही ढंग से काम करने की आवश्यकता है, तो बोलें एक ओवरराइड होना चाहिए, एक नया नहीं। अगर बच्चे को 99% आधार की कार्यक्षमता की आवश्यकता होती है, लेकिन एक बात के मामले में, इसे सुपरबेस की कार्यक्षमता की आवश्यकता है ... इस तरह की स्थिति मैं ओपी के बारे में बात करना समझता हूं, जिस स्थिति में यह विधि काम नहीं करेगी। यह असामान्य नहीं है और जिन सुरक्षा चिंताओं ने C # के व्यवहार को जन्म दिया है, वे वास्तव में बहुत चिंतित नहीं हैं।
शाविस

नियंत्रण और ईवेंट कॉल चेन के मामले में बस एक नोट, विधियां अक्सर संरक्षित होती हैं और इसलिए इस तरह से सुलभ नहीं होती हैं।
शिव

2
यह वंशानुक्रम का उपयोग नहीं कर रहा है, आप प्रत्येक स्पीक को पूरी तरह से विशिष्ट नाम दे सकते हैं।
निक सोत्रोस

हाँ, यह 100% वंशानुक्रम नहीं है, लेकिन यह माता
क्रुकज़कोव्स्की

5
public class A
{
    public int i = 0;
    internal virtual void test()
    {
        Console.WriteLine("A test");
    }
}

public class B : A
{
    public new int i = 1;
    public new void test()
    {
        Console.WriteLine("B test");
    }
}

public class C : B
{
    public new int i = 2;
    public new void test()
    {
        Console.WriteLine("C test - ");
        (this as A).test(); 
    }
}

3

आप ग्रैंड बेस फ़ंक्शन को कॉल करने के लिए पहले स्तर पर व्युत्पन्न वर्ग में एक साधारण फ़ंक्शन भी बना सकते हैं


वास्तव में ऐसा है, और यह पूरी अमूर्त योजना को संरक्षित करता है जिसके बारे में हर कोई बहुत चिंतित है, और यह इस बात पर प्रकाश डालता है कि अमूर्त योजनाएं कभी-कभी अधिक परेशान होती हैं क्योंकि वे इसके लायक हैं।
शाविस

3

इसके लिए मेरा 2c उस कार्यशीलता को लागू करने के लिए है जिसे आपको टूलकिट क्लास में बुलाया जाना चाहिए और जहाँ भी आपको आवश्यकता हो, कॉल करें:

// Util.cs
static class Util 
{
    static void DoSomething( FooBase foo ) {}
}

// FooBase.cs
class FooBase
{
    virtual void Do() { Util.DoSomething( this ); }
}


// FooDerived.cs
class FooDerived : FooBase
{
    override void Do() { ... }
}

// FooDerived2.cs
class FooDerived2 : FooDerived
{
    override void Do() { Util.DoSomething( this ); }
}

विशेषाधिकार प्राप्त करने के लिए कुछ विचार की आवश्यकता होती है, internalकार्यक्षमता को सुविधाजनक बनाने के लिए आपको कुछ एक्सेसर विधियों को जोड़ने की आवश्यकता हो सकती है ।


1

ऐसे मामलों में जहां आपके पास व्युत्पन्न वर्ग स्रोत तक पहुंच नहीं है, लेकिन मौजूदा विधि के अलावा व्युत्पन्न वर्ग के सभी स्रोत की आवश्यकता है, तो मैं आपको एक व्युत्पन्न वर्ग भी करना चाहिए और व्युत्पन्न वर्ग के कार्यान्वयन को कॉल करना चाहिए।

यहाँ एक उदाहरण है:

//No access to the source of the following classes
public class Base
{
     public virtual void method1(){ Console.WriteLine("In Base");}
}
public class Derived : Base
{
     public override void method1(){ Console.WriteLine("In Derived");}
     public void method2(){ Console.WriteLine("Some important method in Derived");}
}

//Here should go your classes
//First do your own derived class
public class MyDerived : Base
{         
}

//Then derive from the derived class 
//and call the bass class implementation via your derived class
public class specialDerived : Derived
{
     public override void method1()
     { 
          MyDerived md = new MyDerived();
          //This is actually the base.base class implementation
          MyDerived.method1();  
     }         
}

0

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

public class Base
{
    public virtual void Foo()
    {
        Console.WriteLine("Hello from Base");
    }
}

public class Derived : Base
{
    public override void Foo()
    {
        base.Foo();
        Console.WriteLine("Text 1");
        WriteText2Func();
        Console.WriteLine("Text 3");
    }

    protected virtual void WriteText2Func()
    {  
        Console.WriteLine("Text 2");  
    }
}

public class Special : Derived
{
    public override void WriteText2Func()
    {
        //WriteText2Func will write nothing when 
        //method Foo is called from class Special.
        //Also it can be modified to do something else.
    }
}

0

ऐसा लगता है कि एक दादा दादी वर्ग से एक सदस्य विधि विरासत में मिली है, एक दूसरी कक्षा में इसे ओवरराइड करने, फिर एक पोते की कक्षा से फिर से अपने तरीके को बुलाते हुए बहुत सारे सवाल हैं। सिर्फ दादा-दादी के सदस्यों को पोते से विरासत में क्यों नहीं मिला?

class A
{
    private string mystring = "A";    
    public string Method1()
    {
        return mystring;
    }
}

class B : A
{
    // this inherits Method1() naturally
}

class C : B
{
    // this inherits Method1() naturally
}


string newstring = "";
A a = new A();
B b = new B();
C c = new C();
newstring = a.Method1();// returns "A"
newstring = b.Method1();// returns "A"
newstring = c.Method1();// returns "A"

सरल लगता है .... पोते को यहाँ दादा दादी विधि विरासत में मिली। इसके बारे में सोचो ..... कि कैसे "ऑब्जेक्ट" और इसके सदस्यों की तरह ToString () C # में सभी वर्गों को विरासत में मिला है। मुझे लगता है कि Microsoft ने मूल विरासत को समझाने का अच्छा काम नहीं किया है। बहुरूपता और कार्यान्वयन पर बहुत अधिक ध्यान केंद्रित है। जब मैं उनके प्रलेखन के माध्यम से खुदाई करता हूं तो इस बहुत ही मूल विचार के कोई उदाहरण नहीं हैं। :(


-2

यदि आप बेस क्लास डेटा तक पहुँच चाहते हैं, तो आपको "इस" कीवर्ड का उपयोग करना चाहिए या आप इस कीवर्ड को क्लास के संदर्भ के रूप में उपयोग करें।

namespace thiskeyword
{
    class Program
    {
        static void Main(string[] args)
        {
            I i = new I();
            int res = i.m1();
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }

    public class E
    {
        new public int x = 3;
    }

    public class F:E
    {
        new public int x = 5;
    }

    public class G:F
    {
        new public int x = 50;
    }

    public class H:G
    {
        new public int x = 20;
    }

    public class I:H
    {
        new public int x = 30;

        public int m1()
        {
           // (this as <classname >) will use for accessing data to base class

            int z = (this as I).x + base.x + (this as G).x + (this as F).x + (this as E).x; // base.x refer to H
            return z;
        }
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.