2008-10-31 10 views
27

निम्नलिखित कोड में, मैं इसे प्राप्त करने/उप-वर्गीकरण करके कक्षा के व्यवहार को विस्तारित करना चाहता हूं, और बेस क्लास की एक घटना का उपयोग करना चाहता हूं:क्यों सी # में बेस क्लास में व्युत्पन्न कक्षाओं में घटनाओं का उपयोग नहीं किया जा सकता है?

public class A 
{ 
    public event EventHandler SomeEvent; 

    public void someMethod() 
    { 
     if(SomeEvent != null) SomeEvent(this, someArgs); 
    } 
} 

public class B : A 
{ 
    public void someOtherMethod() 
    { 
     if(SomeEvent != null) SomeEvent(this, someArgs); // << why is this not possible? 
//Error: The event 'SomeEvent' can only appear on the left hand side of += or -= 
//(except when used from within the type 'A') 
    } 
} 

यह क्यों संभव नहीं है?

और इस तरह की स्थिति के लिए सामान्य समाधान क्या है?

उत्तर

29

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

Jon Skeet's उत्तर या C# specification पढ़ने के स्पष्टीकरण के लिए जो वर्णन करता है कि संकलक स्वचालित रूप से एक निजी क्षेत्र कैसे बनाता है।

यहां एक संभव काम है।

public class A 
{ 
    public event EventHandler SomeEvent; 

    public void someMethod() 
    { 
     OnSomeEvent(); 
    } 

    protected void OnSomeEvent() 
    { 
     EventHandler handler = SomeEvent; 
     if(handler != null) 
      handler(this, someArgs); 
    } 
} 

public class B : A 
{ 
    public void someOtherMethod() 
    { 
     OnSomeEvent(); 
    } 
} 

संपादित करें: अपडेट किया गया कोड Framework Design Guidelines section 5.4 और reminders दूसरों के द्वारा पर आधारित है।

+0

उपसर्ग का प्रयोग न करें 'उठाएँ ...', यह हमेशा होना चाहिए 'पर ...'। इसके अलावा बेस क्लास की विधि वर्चुअल होना चाहिए। – Keith

+0

मल्टीथ्रेडिंग के संकेत के लिए भी धन्यवाद। –

+0

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

1

साथ एक संरक्षित आभासी पर ... विधि यह लपेटें:

public class BaseClass 
{ 
    public event EventHandler<MyArgs> SomeEvent; 

    protected virtual void OnSomeEvent() 
    { 
     if(SomeEvent!= null) 
      SomeEvent(this, new MyArgs(...)); 
    } 
} 

फिर एक व्युत्पन्न वर्ग

public class DerivedClass : BaseClass 
{ 
    protected override void OnSomeEvent() 
    { 
     //do something 

     base.OnSomeEvent(); 
    } 
} 

आप सभी नेट पर इस पैटर्न सेट करेंगे में से ओवरराइड - सभी प्रपत्र और वेब नियंत्रण इसका पालन करें।

उपसर्ग का उपयोग न करें ... - यह एमएस के मानकों के अनुरूप नहीं है और कहीं और भ्रम पैदा कर सकता है।

40

अन्य ने समझाया है कि इस मुद्दे को कैसे प्राप्त किया जाए, लेकिन यह क्यों नहीं आ रहा है।

जब आप सार्वजनिक क्षेत्र की तरह ईवेंट घोषित करते हैं, तो संकलक एक सार्वजनिक कार्यक्रम और एक निजी क्षेत्र बनाता है। एक ही कक्षा के भीतर (या नेस्टेड कक्षाएं) आप सीधे मैदान पर जा सकते हैं, उदा। सभी हैंडलरों को आह्वान करने के लिए। अन्य कक्षाओं से, आप केवल ईवेंट देखते हैं, जो केवल सदस्यता और सदस्यता रद्द करने की अनुमति देता है।

+0

बहुत दिलचस्प है। क्या आपके पास प्रासंगिक दस्तावेज का लिंक है? – biozinc

+1

+1, जटिलताओं पर अच्छी जानकारी। –

+1

इस तरह के दस्तावेज़ीकरण नहीं है, लेकिन http://pobox.com/~skeet/csharp/events.html सामान्य रूप से ईवेंट और प्रतिनिधियों के बारे में अधिक जानकारी प्राप्त करता है। –

5

टोड का जवाब सही है। अक्सर आप इस OnXXX(EventArgs) तरीके के रूप में नेट ढांचा भर कार्यान्वित देखेंगे:

public class Foo 
{ 
    public event EventHandler Click; 

    protected virtual void OnClick(EventArgs e) 
    { 
     var click = Click; 
     if (click != null) 
      click(this, e); 
    } 
} 

मैं दृढ़ता से EventArgs<T>/EventHandler<T> pattern पर विचार करने के लिए प्रोत्साहित करते हैं इससे पहले कि आप अपने आप को लगता है की घटनाओं को उठाने के लिए CustomEventArgs/CustomEventHandler के सभी तरह बना रही है।

2

मूल कोड काम नहीं करता है क्योंकि आपको इसे बढ़ाने के लिए ईवेंट के प्रतिनिधि तक पहुंच की आवश्यकता है, और सी # इस प्रतिनिधि को private रखता है।

सी # में घटनाओं को सार्वजनिक रूप से विधियों, add_SomeEvent और remove_SomeEvent द्वारा सार्वजनिक रूप से दर्शाया जाता है, यही कारण है कि आप कक्षा के बाहर से किसी ईवेंट की सदस्यता ले सकते हैं, लेकिन इसे उठा नहीं सकते हैं।

2

मेरा उत्तर यह होगा कि आपको ऐसा नहीं करना चाहिए।

सी # अच्छी तरह से लागू करता है केवल घटना को घोषित/प्रकाशित करने के प्रकार को आग लगाना चाहिए। यदि बेस क्लास ने अपनी घटनाओं को बढ़ाने की क्षमता रखने के लिए विश्वसनीय व्युत्पन्न किया है, तो निर्माता ऐसा करने के लिए संरक्षित तरीकों का पर्दाफाश करेगा। यदि वे मौजूद नहीं हैं, तो यह एक अच्छा संकेत है कि आपको शायद यह नहीं करना चाहिए।

मेरा योगदान उदाहरण यह है कि दुनिया के कितने अलग होंगे यदि व्युत्पन्न प्रकारों को उनके पूर्वजों में घटनाओं को बढ़ाने की अनुमति दी गई थी। नोट: इस नहीं वैध सी # कोड है .. (अभी तक ..)

public class GoodVigilante 
{ 
    public event EventHandler LaunchMissiles; 

    public void Evaluate() 
    { 
    Action a = DetermineCourseOfAction(); // method that evaluates every possible 
// non-violent solution before resorting to 'Unleashing the fury' 

    if (null != a) 
    { a.Do(); } 
    else 
    { if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); } 
    } 

    virtual protected string WhatsTheTime() 
    { return DateTime.Now.ToString(); } 
    .... 
} 
public class TriggerHappy : GoodVigilante 
{ 
    protected override string WhatsTheTime() 
    { 
    if (null != LaunchMissiles) LaunchMissiles(this, EventArgs.Empty); 
    } 

} 

// client code 
GoodVigilante a = new GoodVigilante(); 
a.LaunchMissiles += new EventHandler(FireAway); 
GoodVigilante b = new TriggerHappy();    // rogue/imposter 
b.LaunchMissiles += new EventHandler(FireAway); 

private void FireAway(object sender, EventArgs e) 
{ 
    // nuke 'em 
} 
संबंधित मुद्दे