2009-04-07 5 views
52

मैं सी # में घटनाक्रम/प्रतिनिधियों के बारे में सीख रहा हूं। क्या मैं आपके द्वारा चुनी गई नामकरण/कोडिंग शैली (हेड फर्स्ट सी # पुस्तक से लिया गया) पर आपकी राय पूछ सकता हूं?घटनाक्रम - नामकरण सम्मेलन और शैली

मैं इस बारे में एक दोस्त को पढ़ रहा हूं, और अवधारणाओं को समझाने के सबसे सुंदर तरीके से आने की कोशिश कर रहा हूं। (सोचा एक विषय को समझने के लिए सबसे अच्छा तरीका है कोशिश करते हैं और यह सिखाने के लिए है!)

class Program 
    { 
     static void Main() 
     { 
      // setup the metronome and make sure the EventHandler delegate is ready 
      Metronome metronome = new Metronome(); 

      // wires up the metronome_Tick method to the EventHandler delegate 
      Listener listener = new Listener(metronome); 
      metronome.OnTick(); 
     } 
    } 

public class Metronome 
    { 
     // a delegate 
     // so every time Tick is called, the runtime calls another method 
     // in this case Listener.metronome_Tick 
     public event EventHandler Tick; 

     public void OnTick() 
     { 
      while (true) 
      { 
       Thread.Sleep(2000); 
       // because using EventHandler delegate, need to include the sending object and eventargs 
       // although we are not using them 
       Tick(this, EventArgs.Empty); 
      } 
     } 
    } 

public class Listener 
    { 
     public Listener(Metronome metronome) 
     { 
      metronome.Tick += new EventHandler(metronome_Tick); 
     } 

     private void metronome_Tick(object sender, EventArgs e) 
     { 
      Console.WriteLine("Heard it"); 
     } 
    } 

N.B. कोड http://www.codeproject.com/KB/cs/simplesteventexample.aspx

उत्तर

46

कुछ बिंदुओं कि मैं उल्लेख होगा रहे हैं। अर्थात्, "ऑनटिक" मुझे बताता है कि इसे "टिक" के रूप में बुलाया जाएगा, लेकिन वास्तव में यह नहीं हो रहा है कि क्या हो रहा है। मैं इसके बजाय इसे "जाओ" कहूंगा।

आम तौर पर स्वीकृत मॉडल, हालांकि निम्नलिखित करना होगा। OnTick एक वर्चुअल विधि है जो ईवेंट को बढ़ाती है। इस तरह, आप आसानी से विरासत कक्षाओं में डिफ़ॉल्ट व्यवहार को ओवरराइड कर सकते हैं, और ईवेंट को बढ़ाने के लिए आधार पर कॉल कर सकते हैं।

class Metronome 
{ 
    public event EventHandler Tick; 

    protected virtual void OnTick(EventArgs e) 
    { 
     //Raise the Tick event (see below for an explanation of this) 
     var tickEvent = Tick; 
     if(tickEvent != null) 
      tickEvent(this, e); 
    } 

    public void Go() 
    { 
     while(true) 
     { 
      Thread.Sleep(2000); 
      OnTick(EventArgs.Empty); //Raises the Tick event 
     } 
    } 
} 

इसके अलावा, मैं जानता हूँ कि यह एक सरल उदाहरण है, लेकिन अगर कोई श्रोताओं संलग्न कर रहे हैं, अपने कोड Tick(this, EventArgs.Empty) पर फेंक देते हैं। आपको कम से कम श्रोताओं के लिए जाँच करने के लिए एक अशक्त गार्ड शामिल करना चाहिए:

if(Tick != null) 
    Tick(this, EventArgs.Empty); 

बहरहाल, यह अभी भी एक बहु-क्रम वातावरण में कमजोर है अगर श्रोता गार्ड और मंगलाचरण के बीच अपंजीकृत है। सबसे अच्छा पहला वर्तमान श्रोताओं को पकड़ने और उन्हें फोन करने के लिए होगा:

var tickEvent = Tick; 
if(tickEvent != null) 
    tickEvent(this, EventArgs.Empty); 

मैं जानता हूँ कि यह एक पुरानी जवाब है, लेकिन जब से यह अभी भी upvotes सभा है, यहां काम करने के सी # 6 तरीका है। पूरी "गार्ड" की अवधारणा एक सशर्त विधि कॉल के साथ प्रतिस्थापित किया जा सकता है और संकलक वास्तव में श्रोताओं पर कब्जा करने के संबंध में सही काम (टीएम) क्या करता है:

Tick?.Invoke(this, EventArgs.Empty); 
+12

सुरक्षा के लिए वैकल्पिक "= प्रतिनिधि {};" जोड़ना है घोषणा करने के लिए (देखें http://stackoverflow.com/questions/231525/raising-c-events-with-an-extension-method-is-it-bad/231536#231536) – Benjol

+0

ध्यान दें कि भेद्यता _limited_ नहीं है बहुप्रचारित वातावरण। किसी ईवेंट से सभी हैंडलर को निकालने के लिए एक ईवेंट हैंडलर के लिए यह संभव है (यदि सोसायपाथिक), जिसके परिणामस्वरूप हैंडलर पूरा हो जाता है और ईवेंट आमंत्रण अगले (अब मौजूद नहीं) ईवेंट को चलाने का प्रयास करता है। –

+0

@GregD: क्या मूर्खतापूर्ण तरीके से ऐसा करने का कोई तरीका है कि क्लाइंट कोड ऐसा नहीं कर सका? –

2

इस तथ्य से अलग है कि OnTick सामान्य घटना आमंत्रण मॉडल का पालन नहीं करता है। आमतौर पर, On[EventName] घटना, एक ही समय को जन्म देती है जैसे

protected virtual void OnTick(EventArgs e) 
{ 
    if(Tick != null) Tick(this, e); 
} 

इस विधि का निर्माण, और अपने मौजूदा "OnTick" विधि का नाम बदलने के लिए "StartTick", और बदले Tick सीधे लागू StartTick से की, StartTick से OnTick(EventArgs.Empty) फोन पर विचार करें तरीका।

Metronome.OnTick सही ढंग से नामित किया जाना प्रतीत नहीं होता:

4

एक बिंदु मैं में घटनाओं उपयोग करने के बाद मिल गया है। कई वर्षों के लिए नेट प्रत्येक आमंत्रण पर एक नल हैंडलर के लिए घटना की जांच करने की दोहराव की आवश्यकता है। मुझे अभी तक लाइव कोड का एक टुकड़ा नहीं दिख रहा है जो कुछ भी करता है लेकिन अगर यह शून्य है तो ईवेंट को कॉल न करें।

मैंने जो करना शुरू कर दिया है वह है कि मैं हर घटना पर एक डमी हैंडलर डालूं जिसे मैं शून्य जांच करने की आवश्यकता को बचाने के लिए बनाता हूं।

public class Metronome 
{ 
    public event EventHandler Tick =+ (s,e) => {}; 

    protected virtual void OnTick(EventArgs e) 
    { 
     Tick(this, e); // now it's safe to call without the null check. 
    } 
} 
+1

इंगित करने के लिए एक बात यह है कि यह क्रमबद्धता के साथ काम नहीं करता है। खाली प्रतिनिधि विचार पर http://stackoverflow.com/questions/9033/hidden-features-of-c/9282#9282 –

+1

पर पूरी तरह से चर्चा की गई है यह आसान हो सकता है: 'सार्वजनिक ईवेंट इवेंट हैंडलर टिक = प्रतिनिधि {}; ' – Mikhail

56

माइक्रोसॉफ्ट ने वास्तव में नामकरण दिशानिर्देशों का व्यापक सेट लिखा है और इसे एमएसडीएन पुस्तकालय में रखा है। सामान्य कैपिटलाइज़ेशन दिशानिर्देश के अलावा Guidelines for Names

, यहाँ क्या यह पेज Names of Type Members पर 'ईवेंट' के लिए है: आप यहाँ लेख मिल सकते हैं

एक क्रिया या एक क्रिया वाक्यांश के साथ नाम घटनाओं करें ।

वर्तमान और पिछले काल का उपयोग करके घटनाओं को की अवधारणा दें। उदाहरण के लिए, एक बंद विंडो जो विंडो से पहले उठाई गई है उसे बंद कर दिया जाएगा और विंडो के बाद उठाए गए एक को बंद कर दिया जाएगा बंद कर दिया जाएगा।

उपसर्ग या प्रत्यय से पहले या बाद में ईवेंट को इंगित करने के लिए प्रत्यय का उपयोग न करें।

EventHandler प्रत्यय के साथ नाम ईवेंट हैंडलर (प्रतिनिधियों ने ईवेंट के प्रकार के रूप में उपयोग किया) नाम दें।

प्रेषक नामक दो पैरामीटर और ई ईवेंट हैंडलर हस्ताक्षर में उपयोग करें।

इस पैरामीटर के प्रकार वस्तु होना चाहिए, और ई पैरामीटर का एक उदाहरण है या EventArgs से विरासत चाहिए।

इवेंटअर्ग प्रत्यय के साथ ईवेंट इवेंट तर्क कक्षाएं करें।

12

मैं कहूंगा कि सामान्य रूप में घटनाओं, नामकरण रिवाजों का भी शामिल है, के लिए सबसे अच्छा गाइड here है।

यह है सम्मेलन मैं अपनाया है, संक्षेप में:

  • घटनाक्रम के नाम आम तौर पर (बंद/बंद, लोड हो रहा है/लोड हो गई)
  • वर्ग एक क्रिया -ing के साथ समाप्त होने या एड के साथ समाप्त कर दिया जाता है जो घोषणा करता है कि ईवेंट में एक संरक्षित वर्चुअल होना चाहिए [EventName] जिसे ईवेंट को बढ़ाने के लिए शेष वर्ग द्वारा उपयोग किया जाना चाहिए। घटना को बढ़ाने के लिए उप-वर्गों द्वारा इस विधि का भी उपयोग किया जा सकता है, और ईवेंट-राइजिंग तर्क को संशोधित करने के लिए अधिभारित किया जा सकता है।
  • वहाँ अक्सर है 'हैंडलर' के उपयोग के बारे में भ्रम की स्थिति - जुटना के लिए, सभी प्रतिनिधियों हैंडलर साथ postfixed किया जाना चाहिए, तरीकों जो विधि के लिए नामकरण परिपाटी वी.एस. हैंडलर 'संचालकों'
  • डिफ़ॉल्ट लागू बुला से बचने की कोशिश जो हैंडलर लागू करता है EventPublisherName_EventName है।
5

दिलचस्प स्टूडियो जेनरेट किए गए ईवेंट हैंडलर नामों के साथ माइक्रोसॉफ्ट अपने नामकरण सम्मेलनों को तोड़ने लगता है।

देखें: Event Naming Guidelines (.NET Framework 1.1)

2

आपके मामले में यह हो सकता है:

class Metronome { 
    event Action Ticked; 

    internalMethod() { 
    // bla bla 
    Ticked(); 
    } 
} 

सम्मेलन नीचे sampple उपयोग से ऊपर, स्वयं का वर्णन;]

घटनाक्रम स्रोत:

class Door { 

    // case1: property change, pattern: xxxChanged 
    public event Action<bool> LockStateChanged; 

    // case2: pure action, pattern: "past verb" 
    public event Action<bool> Opened; 

    internalMethodGeneratingEvents() { 
    // bla bla ... 

    Opened(true); 
    LockStateChanged(false); 
    } 

} 

बीटीडब्ल्यू। कीवर्ड event वैकल्पिक है, लेकिन अलग-अलग से 'कॉलबैक'

घटनाक्रम श्रोता 'घटनाओं' सक्षम बनाता है:

door.LockStateChanged += alarmManager.NotifyLockStateChanged; 
door.Moved += alarmManager.NotifyDoorOpened; 

भी मैन्युअल रूप से घटनाओं भेज रहा है "मानव [मानव अनुकूल लग रहा है कोड]

class AlarmManager { 

    // pattern: NotifyXxx 
    public NotifyLockStateChanged(bool state) { 
    // ... 
    } 

    // pattern: [as above]  
    public NotifyOpened(bool opened) { 
    // OR 
    public NotifyDoorOpened(bool opened) { 
    // ... 
    } 

} 

और बाध्यकारी पठनीय "।

alarmManager.NotifyDoorOpened(true); 

कभी कभी अधिक अर्थपूर्ण "क्रिया + ing"

dataGenerator.DataWaiting += dataGenerator.NotifyDataWaiting; 

जो भी सम्मेलन आप चुनते हैं हो सकता है, यह के अनुरूप होना।