2009-12-05 5 views
65

क्या गैर-वर्चुअल विधि को ओवरराइड करने का कोई तरीका है? या ऐसा कुछ जो समान परिणाम देता है (वांछित विधि को कॉल करने के लिए एक नई विधि बनाने के अलावा)?क्या गैर-वर्चुअल विधि को ओवरराइड करना संभव है?

मैं इकाई परीक्षण के साथ Microsoft.Xna.Framework.Graphics.GraphicsDevice से एक विधि को ओवरराइड करना चाहता हूं।

+7

क्या आपका मतलब है * ओवरलोड * या * ओवरराइड *? अधिभार = एक ही नाम के साथ एक विधि जोड़ें लेकिन विभिन्न पैरामीटर (जैसे Console.WriteLine के विभिन्न अधिभार)। ओवरराइड = (मोटे तौर पर) विधि के डिफ़ॉल्ट व्यवहार को बदलें (उदा। एक आकार। ड्रॉ विधि जिसमें मंडल, आयत, इत्यादि के लिए अलग-अलग व्यवहार हैं)। आप हमेशा व्युत्पन्न वर्ग में एक विधि * ओवरलोड * कर सकते हैं, लेकिन * ओवरराइडिंग * केवल आभासी तरीकों पर लागू होता है। – itowlson

उत्तर

81

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

लेकिन यहां तक ​​कि एक विधि को छिपाने से आपको सही वर्चुअल विधि कॉल की तरह विधि कॉल के निष्पादन समय पॉलिमॉर्फिक प्रेषण नहीं मिलेगा।

using System; 

class Example 
{ 
    static void Main() 
    { 
     Foo f = new Foo(); 
     f.M(); 

     Foo b = new Bar(); 
     b.M(); 
    } 
} 

class Foo 
{ 
    public void M() 
    { 
     Console.WriteLine("Foo.M"); 
    } 
} 

class Bar : Foo 
{ 
    public new void M() 
    { 
     Console.WriteLine("Bar.M"); 
    } 
} 

M विधि प्रिंट Foo.M के दोनों कॉल इस उदाहरण में: इस उदाहरण पर विचार। जैसा कि आप देख सकते हैं कि यह दृष्टिकोण आपको विधि के लिए एक नया कार्यान्वयन करने की अनुमति देता है जब तक कि उस ऑब्जेक्ट का संदर्भ सही व्युत्पन्न प्रकार का होता है लेकिन बेस विधि छुपाता है ब्रेक पॉलिमॉर्फिज्म करता है।

मैं अनुशंसा करता हूं कि आप इस तरीके से आधार विधियों को छिपाएं।

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

संपादित करें: "निष्पादन समय बहुरूपी प्रेषण":

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

अगर मैं उस मामले में b.Foo कॉल करने के लिए थे, CLR सही ढंग से वस्तु के प्रकार है कि Bar के रूप में b संदर्भ बिंदु और उचित रूप से M करने के लिए कॉल प्रेषण होगा निर्धारित करेंगे।

+4

हालांकि "निष्पादन समय polymorphic प्रेषण" तकनीकी रूप से यह कहने का सही तरीका है, मुझे लगता है कि यह शायद लगभग सभी के सिर पर चला जाता है! –

+1

हालांकि यह सच है कि लेखक को ओवरराइड करने की विधि को अस्वीकार करने का इरादा हो सकता है, यह सच नहीं है कि यह करना सही काम था। मुझे लगता है कि एक्सएनए टीम को आईग्राफिक्सडिवाइस इंटरफेस को लागू करना चाहिए ताकि उपयोगकर्ता को डीबगिंग और यूनिट परीक्षण में अधिक लचीलापन मिल सके। मुझे कुछ बदसूरत चीजें करने के लिए मजबूर किया गया है और यह टीम द्वारा पहले से ही देखा जाना चाहिए था। आगे की चर्चा यहां मिल सकती है: http://forums.xna.com/forums/p/30645/226222.aspx – zfedoran

+0

@ ओरियन - पर्याप्त मेला, मैंने एक स्पष्टीकरण जोड़ा है :) –

17

नहीं आप नहीं कर सकते।

आप केवल एक आभासी विधि ओवरराइड कर सकते हैं - देख MSDN here:

सी # में, व्युत्पन्न वर्ग आधार वर्ग तरीके के रूप में एक ही नाम के साथ तरीकों हो सकते हैं।

  • बेस क्लास विधि को आभासी परिभाषित किया जाना चाहिए।
4

आधार वर्ग तो सील नहीं है, तो आप इसे से विरासत और एक नई पद्धति है कि आधार एक (विधि घोषणा में 'नई' कीवर्ड का उपयोग करें) छुपाता लिख ​​सकते हैं। अन्यथा नहीं, आप इसे ओवरराइड नहीं कर सकते क्योंकि यह कभी भी मूल लेखकों के लिए ओवरराइड होने का इरादा नहीं था, इसलिए यह वर्चुअल क्यों नहीं है।

3

मुझे लगता है कि आप ओवरलोडिंग और उलझन में ओवरराइड कर रहे हैं, ओवरलोडिंग का मतलब है कि आपके पास एक ही नाम के साथ दो या दो से अधिक विधियां हैं लेकिन ओवरराइडिंग के दौरान पैरामीटर के विभिन्न सेट का मतलब है कि आपके पास व्युत्पन्न वर्ग में एक विधि के लिए एक अलग कार्यान्वयन है (इस प्रकार प्रतिस्थापन या इसके आधार वर्ग में व्यवहार को संशोधित करना)।

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

कभी भी किसी मौजूदा वर्ग में अधिभार जोड़ने से आपको रोक नहीं रहा है, लेकिन केवल आपके कोड के बारे में जो कोड जानता है वह इसे एक्सेस कर पाएगा।

-4

अमूर्त वर्ग और सार विधि का उपयोग करके इसे प्राप्त करने का एक तरीका है।

Class Base 
{ 
    void MethodToBeTested() 
    { 
     ... 
    } 

    void Method1() 
    { 
    } 

    void Method2() 
    { 
    } 

    ... 
} 

पर विचार करें अब, अगर आप विधि MethodToBeTested(), तो परिवर्तन कक्षा एक सार विधि

abstract Class Base 
{ 

    abstract void MethodToBeTested(); 

    void Method1() 
    { 
    } 

    void Method2() 
    { 
    } 

    ... 
} 
के रूप में एक अमूर्त वर्ग और विधि MethodToBeTested() करने के लिए बेस के विभिन्न संस्करणों करना चाहते हैं

अमूर्त शून्य के साथ MethodToBeTested() एक मुद्दा आता है; कार्यान्वयन चला गया है।

इसलिए डिफ़ॉल्ट कार्यान्वयन के लिए class DefaultBaseImplementation : Base बनाएं।

और यूनिट परीक्षण कार्यान्वयन के लिए एक और class UnitTestImplementation : Base बनाएं।

इन 2 नए वर्गों के साथ, बेस क्लास कार्यक्षमता को ओवरराइड किया जा सकता है।

Class DefaultBaseImplementation : Base  
{ 
    override void MethodToBeTested()  
    {  
     //Base (default) implementation goes here  
    } 

} 

Class UnitTestImplementation : Base 
{ 

    override void MethodToBeTested()  
    {  
     //Unit test implementation goes here  
    } 

} 

अब आप 2 वर्गों को लागू करने (अधिभावी) MethodToBeTested() है।

आप आवश्यकतानुसार (व्युत्पन्न) कक्षा को तुरंत चालू कर सकते हैं (यानी आधार कार्यान्वयन या इकाई परीक्षण कार्यान्वयन के साथ)।

+0

@ स्लावू: हाय स्लावू। कोड अपडेट के लिए धन्यवाद। लेकिन क्या आप कृपया मुझे इसे कम करने का कारण बता सकते हैं? – ShivanandSK

+3

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

0

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

0

क्या गैर-वर्चुअल विधि को ओवरराइड करने का कोई तरीका है? या ऐसा कुछ जो समान परिणाम देता है (वांछित विधि को कॉल करने के लिए एक नई विधि बनाने के अलावा)?

आप गैर-वर्चुअल विधि को ओवरराइड नहीं कर सकते हैं। लेकिन अगर आप new संशोधक कीवर्ड का उपयोग कर सकते इसी तरह के परिणाम प्राप्त करने के लिए:

class Class0 
{ 
    public int Test() 
    { 
     return 0; 
    } 
} 

class Class1 : Class0 
{ 
    public new int Test() 
    { 
     return 1; 
    } 
} 
. . . 
// result of 1 
Console.WriteLine(new Class1().Test()); 

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

तो पहुँच संशोधक समान नहीं है:

class Class0 
{ 
    protected int Test() 
    { 
     return 0; 
    } 
} 

class Class1 : Class0 
{ 
    // different access modifier 
    new int Test() 
    { 
     return 1; 
    } 
} 

class Class2 : Class1 
{ 
    public int Result() 
    { 
     return Test(); 
    } 
} 
. . . 
// result of 0 
Console.WriteLine(new Class2().Result()); 

... बनाम यदि पहुँच संशोधक एक ही है:

class Class0 
{ 
    protected int Test() 
    { 
     return 0; 
    } 
} 

class Class1 : Class0 
{ 
    // same access modifier 
    protected new int Test() 
    { 
     return 1; 
    } 
} 

class Class2 : Class1 
{ 
    public int Result() 
    { 
     return Test(); 
    } 
} 
. . . 
// result of 1 
Console.WriteLine(new Class2().Result()); 

के रूप में पिछले एक जवाब में बताया , यह एक अच्छा डिजाइन सिद्धांत नहीं है।