2010-01-27 13 views
43

मुझे लगता है अपने आप को बात की इस तरह अक्सर कर रही: -एक शॉट घटनाओं सी # में लैम्ब्डा का उपयोग कर

EventHandler eh = null; //can't assign lambda directly since it uses eh 
eh = (s, args) => 
{ 
    //small snippet of code here 

    ((SomeType)s).SomeEvent -= eh; 
} 
variableOfSomeType.SomeEvent += eh; 

असल में मैं केवल एक ईवेंट हैंडलर संलग्न करने के लिए घटना से एक शॉट के लिए सुनना चाहते हैं, मैं अब उसके बाद संलग्न रहना चाहते हैं। अक्सर "कोड का स्निपेट" सिर्फ एक पंक्ति है।

मेरा दिमाग थोड़ी सुस्त हो रहा है, मुझे यकीन है कि ऐसा कुछ होना चाहिए जो मैं कर सकता हूं इसलिए मुझे इस सबहेड को दोहराने की आवश्यकता नहीं है। ध्यान रखें कि EventHandlerEventHandler<T> हो सकता है।

कोई विचार मैं कोड के दोहराव वाले हिस्से को कैसे व्यवस्थित कर सकता हूं और स्नैपेट को लैम्ब्डा में छोड़ सकता हूं?

+0

यह गंध करता है, मैं सहमत हूं। जवाब देखने के लिए तत्पर हैं। ;) – Lucero

+0

स्थायी ईवेंट हैंडलर को जोड़ने के बारे में क्या है जो एक कतार में "एक शॉट इवेंट हैंडलर" को आमंत्रित करता है? – dtb

+0

दिलचस्प; हमने पुशिनक्यू में कुछ ऐसा ही किया; लेकिन पुन: उपयोग के लिए समस्याएं हैं; लैम्ब्डा घटनाओं को नहीं चुन सकते हैं, और घटना के बिना, टाइपिंग इनफ्रेंसिंग एक दर्द है - और हैंडलर के संदर्भ में बिना किसी प्रकार के लैम्बडा ... मजेदार हो सकता है। –

उत्तर

5

यदि आप Reactive Extensions for .NET का उपयोग कर सकते हैं, तो आप इसे सरल बना सकते हैं।

आप Observable from an event बना सकते हैं, और कोड के अपने छोटे स्निपेट को करने के लिए केवल .Take(1) का उपयोग करके पहले तत्व को सुनें। यह इस पूरी प्रक्रिया को कोड की कुछ पंक्तियों में बदल देता है।


संपादित करें: प्रदर्शित करने के लिए, मैंने एक पूर्ण नमूना कार्यक्रम बनाया है (मैं नीचे पेस्ट करूँगा)।

मैंने एक विधि (HandleOneShot) में देखने योग्य निर्माण और सदस्यता को स्थानांतरित कर दिया। यह आपको एक विधि विधि के साथ प्रयास करने की सुविधा देता है। प्रदर्शन के लिए, मैंने दो गुणों के साथ एक वर्ग बनाया है जो INotifyPropertyChanged लागू करता है, और के लिए पहले संपत्ति बदल गया है, जब यह होता है तो कंसोल पर लिखना।

यह आपके कोड लेता है, और करने के लिए यह परिवर्तन:

HandleOneShot<SomeEventArgs>(variableOfSomeType, "SomeEvent", e => { 
        // Small snippet of code here 
       }); 

ध्यान दें कि सदस्यता/सदस्यता खत्म करने के सभी पर्दे के पीछे आप के लिए अपने आप होता है। सदस्यता में मैन्युअल रूप से डालने की कोई आवश्यकता नहीं है - केवल पर्यवेक्षण की सदस्यता लें, और आरएक्स आपके लिए इसका ख्याल रखता है।

जब चलाने के लिए, इस कोड को प्रिंट:

Setup... 
Setting first property... 
**** Prop2 Changed! /new val 
Setting second property... 
Setting first property again. 
Press ENTER to continue... 

आप केवल एक ही, एक शॉट अपने घटना के ट्रिगर मिलता है।

namespace ConsoleApplication1 
{ 
    using System; 
    using System.ComponentModel; 
    using System.Linq; 

    class Test : INotifyPropertyChanged 
    { 
     private string prop2; 
     private string prop; 
     public string Prop 
     { 
      get { 
       return prop; 
      } 
      set 
      { 
       if (prop != value) 
       { 
        prop = value; 
        if (PropertyChanged!=null) 
         PropertyChanged(this, new PropertyChangedEventArgs("Prop")); 
       } 
      } 
     } 

     public string Prop2 
     { 
      get 
      { 
       return prop2; 
      } 
      set 
      { 
       if (prop2 != value) 
       { 
        prop2 = value; 
        if (PropertyChanged != null) 
         PropertyChanged(this, new PropertyChangedEventArgs("Prop2")); 
       } 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 


    class Program 
    { 
     static void HandleOneShot<TEventArgs>(object target, string eventName, Action<TEventArgs> action) where TEventArgs : EventArgs 
     { 
      var obsEvent = Observable.FromEvent<TEventArgs>(target, eventName).Take(1); 
      obsEvent.Subscribe(a => action(a.EventArgs)); 
     } 

     static void Main(string[] args) 
     { 
      Test test = new Test(); 

      Console.WriteLine("Setup..."); 
      HandleOneShot<PropertyChangedEventArgs>(
       test, 
       "PropertyChanged", 
       e => 
        { 
         Console.WriteLine(" **** {0} Changed! {1}/{2}!", e.PropertyName, test.Prop, test.Prop2); 
        }); 

      Console.WriteLine("Setting first property..."); 
      test.Prop2 = "new value"; 
      Console.WriteLine("Setting second property..."); 
      test.Prop = "second value"; 
      Console.WriteLine("Setting first property again..."); 
      test.Prop2 = "other value"; 

      Console.WriteLine("Press ENTER to continue..."); 
      Console.ReadLine(); 
     } 
    } 
} 
+0

मुझे वास्तव में प्रतिक्रियाशील एक्सटेंशन में अभी तक नहीं मिला है, निश्चित रूप से यह कार्यक्षमता प्रदान करेगा जो @ डीटीबी का वर्णन करने लग रहा था, लेकिन फिर भी उस आग पर एक प्रतिनिधि छोड़ देगा जो आग लगती है लेकिन कहीं नहीं जाती? मेरा लक्ष्य अनावश्यक अनुलग्नक को हटाना है या क्या मुझे प्रतिक्रियाशील सामान को बेहतर समझने की आवश्यकता है? – AnthonyWJones

+0

मैं कोड संपादित और पोस्ट करूंगा - यह थोड़ा सा ले रहा है ... –

+0

@ एंथनी WJones: यह लंबा है, लेकिन यह पूरी तरह से काम कर रहा कोड है। इसे चलाने के लिए आपको आरएक्स ढांचे की आवश्यकता होगी, और आरएक्स असेंबली के संदर्भ जोड़ें, हालांकि: http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx –

1

क्या यह काम करता है? यदि ऐसा है, तो मैं इसके लिए कहता हूं। एक-शॉट घटना के लिए जो काफी सुरुचिपूर्ण दिखता है।

मैं क्या पसंद ...

  • रों कचरा एकत्र होता है, इसलिए होगा ईवेंट हैंडलर।
  • डिटेचिंग कोड अटैचिंग कोड के बगल में है, जिससे यह देखना आसान हो जाता है कि आप क्या कर रहे हैं।

आप इसे सामान्यीकृत करने में सक्षम हो सकते हैं, लेकिन मुझे पूरी तरह से यकीन नहीं है कि मुझे ऐसा क्यों करना है क्योंकि मुझे किसी ईवेंट में पॉइंटर नहीं मिल रहा है।

7

आप ईवेंट के लिए एक स्थायी ईवेंट हैंडलर को जोड़ सकते हैं।ईवेंट हैंडलर तो invokes "एक शॉट ईवेंट हैंडलर्स" एक आंतरिक कतार में जोड़े गए:

OneShotHandlerQueue<EventArgs> queue = new OneShotHandlerQueue<EventArgs>(); 

Test test = new Test(); 

// attach permanent event handler 
test.Done += queue.Handle; 

// add a "one shot" event handler 
queue.Add((sender, e) => Console.WriteLine(e)); 
test.Start(); 

// add another "one shot" event handler 
queue.Add((sender, e) => Console.WriteLine(e)); 
test.Start(); 

कोड:

class OneShotHandlerQueue<TEventArgs> where TEventArgs : EventArgs { 
    private ConcurrentQueue<EventHandler<TEventArgs>> queue; 
    public OneShotHandlerQueue() { 
     this.queue = new ConcurrentQueue<EventHandler<TEventArgs>>(); 
    } 
    public void Handle(object sender, TEventArgs e) { 
     EventHandler<TEventArgs> handler; 
     if (this.queue.TryDequeue(out handler) && (handler != null)) 
      handler(sender, e); 
    } 
    public void Add(EventHandler<TEventArgs> handler) { 
     this.queue.Enqueue(handler); 
    } 
} 

टेस्ट वर्ग:

class Test { 
    public event EventHandler Done; 
    public void Start() { 
     this.OnDone(new EventArgs()); 
    } 
    protected virtual void OnDone(EventArgs e) { 
     EventHandler handler = this.Done; 
     if (handler != null) 
      handler(this, e); 
    } 
} 
+0

बस एक और स्तर का संकेत जोड़ें। प्रतिभाशाली! –

+0

काफी अच्छा है। उस उत्तर से लगभग 120 सेकंड में ऊपर और काम करना मुझे यह जवाब मिला। – tster

+0

एक से अधिक प्रविष्टियों के साथ एक कतार के लिए, 'जबकि (queue.TryDeqeue (आउट हैंडलर)) हैंडलर? (यह, ई) ' – bvj

2

अन्य उपयोगकर्ता a very similar problem का सामना करना पड़ा, और मेरा मानना ​​है कि उस धागे का समाधान यहां लागू होता है।

विशेष रूप से, आपके पास प्रकाशित/सब्सक्राइब पैटर्न का एक उदाहरण नहीं है, यह एक संदेश कतार है। Queue{EventHandler} का उपयोग करके अपना स्वयं का संदेश कतार बनाने के लिए पर्याप्त आसान है, जहां आप उन्हें आमंत्रित करते समय ईवेंट को अस्वीकार करते हैं।

तो किसी ईवेंट हैंडलर पर हुक करने की बजाय, आपके "एक-शॉट" ईवेंट को क्लाइंट को संदेश कतार में कोई फ़ंक्शन जोड़ने की अनुमति देने वाली विधि का खुलासा करना चाहिए।

+2

यह मानता है कि आप" variableOfSomeType "के लिए कक्षा लिख ​​रहे हैं, और यह है कि एक ढांचा या तीसरे पक्ष के प्रकार नहीं। यदि यह है, यह काम नहीं करता है। –

7

आप प्रतिबिंब का उपयोग कर सकते हैं:

public static class Listener { 

    public static void ListenOnce(this object eventSource, string eventName, EventHandler handler) { 
    var eventInfo = eventSource.GetType().GetEvent(eventName); 
    EventHandler internalHandler = null; 
    internalHandler = (src, args) => { 
     handler(src, args); 
     eventInfo.RemoveEventHandler(eventSource, internalHandler); 
    }; 
    eventInfo.AddEventHandler(eventSource, internalHandler); 
    } 

    public static void ListenOnce<TEventArgs>(this object eventSource, string eventName, EventHandler<TEventArgs> handler) where TEventArgs : EventArgs { 
    var eventInfo = eventSource.GetType().GetEvent(eventName); 
    EventHandler<TEventArgs> internalHandler = null; 
    internalHandler = (src, args) => { 
     handler(src, args); 
     eventInfo.RemoveEventHandler(eventSource, internalHandler); 
    }; 
    eventInfo.AddEventHandler(eventSource, internalHandler); 
    } 

} 

इतना है कि यह प्रयोग करें:

variableOfSomeType.ListenOnce("SomeEvent", 
    (s, args) => Console.WriteLine("I should print only once!")); 

variableOfSomeType.ListenOnce<InterestingEventArgs>("SomeOtherEvent", 
    (s, args) => Console.WriteLine("I should print only once!")); 
+1

यह वही है जो मैं ढूंढ रहा था। मुझे आश्चर्य है कि कोई भी इस जवाब को रेट नहीं करता है ... – SACO

0

व्यक्तिगत रूप से, मैं सिर्फ एक विशेष विस्तार विधि जो कुछ के लिए प्रकार घटना मैं के साथ काम कर रहा हूँ है बनाएँ।

यहाँ मैं अभी उपयोग कर रहा हूँ कुछ के मूल संस्करण है:

namespace MyLibrary 
{ 
    public static class FrameworkElementExtensions 
    { 
     public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler) 
     { 
      RoutedEventHandler wrapperHandler = null; 
      wrapperHandler = delegate 
      { 
       el.Loaded -= wrapperHandler; 

       handler(el, null); 
      }; 
      el.Loaded += wrapperHandler; 
     } 
    } 
} 

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

namespace MyLibraryOrApplication 
{ 
    public static class FrameworkElementExtensions 
    { 
     public static void HandleWhenLoaded(this FrameworkElement el, RoutedEventHandler handler) 
     { 
      if ((bool)el.GetValue(View.IsLoadedProperty)) 
      { 
       // el already loaded, call the handler now. 
       handler(el, null); 
       return; 
      } 
      // el not loaded yet. Attach a wrapper handler that can be removed upon execution. 
      RoutedEventHandler wrapperHandler = null; 
      wrapperHandler = delegate 
      { 
       el.Loaded -= wrapperHandler; 
       el.SetValue(View.IsLoadedProperty, true); 

       handler(el, null); 
      }; 
      el.Loaded += wrapperHandler; 
     } 
    } 
} 
+0

मुझे यह ध्यान रखना होगा कि यहां एक संभावित दौड़ स्थिति है: यदि आप पृष्ठभूमि-थ्रेड में चल रहे कुछ समान कोड लिखते हैं, तो आप अपने ईवेंट को निकाल दिया जा सकता है। यदि आपकी संपत्ति IsLoadedProperty (उस बिंदु पर झूठी होने के लिए) के बीच लोड हो गई है और बिंदु आप ईवेंट हैंडलर संलग्न करते हैं। - आप इसे यूआई-थ्रेड में यहां चला रहे हैं, इसलिए आपके स्निपेट में कोई समस्या नहीं है। मैं बस गड़बड़ी को इंगित करना चाहता था। – toong

0

शायद आप नए एसिंक/प्रतीक्षा मुहावरे के साथ काम करना चाहते हैं।

await variableOfSomeSort.SomeMethodAsync(); 
//small snippet of code here 
+0

बेशक, यह आपको कुछॉर्ट क्लास पर विधि बनाने के लिए उपयोग करने में सक्षम होने की मांग करेगा। –

0

प्रतिनिधि ढेर घटना में बनाया का उपयोग क्यों नहीं: आमतौर पर जब मैं एक ईवेंट हैंडलर एक शॉट पर अमल करने की तरह आप वर्णित की जरूरत है, कुछ की तरह क्या मैं वास्तव में जरूरत है? कुछ ...

private void OnCheckedIn(object sender, Session e) 
    { 
     EventHandler<Session> nextInLine = null; 
     lock (_syncLock) 
     { 
      if (SessionCheckedIn != null) 
      { 
       nextInLine = (EventHandler<Session>)SessionCheckedIn.GetInvocationList()[0]; 
       SessionCheckedIn -= nextInLine; 
      } 
     } 

     if (nextInLine != null) 
     { 
      nextInLine(this, e); 
     } 
    } 
+1

[इसलिए] में आपका स्वागत है। मैं इस बात का विस्तार करने के लिए फायदेमंद होगा कि यह सवाल क्यों हल करता है। कोड समझाया जाना चाहिए। उत्तर देखो]। – jkalden

संबंधित मुद्दे