2008-09-05 20 views
30

आप गतिशील रूप से सी # घटना की सदस्यता कैसे लेंगे ताकि ऑब्जेक्ट इंस्टेंस और ईवेंट का नाम वाला एक स्ट्रिंग नाम दिया गया हो, तो आप उस ईवेंट की सदस्यता लेते हैं और कुछ करते हैं (उदाहरण के लिए कंसोल पर लिखें) उस घटना को निकाल दिया गया है?सी # गतिशील घटना सदस्यता

यह प्रतिबिंब का उपयोग करना प्रतीत होता है यह संभव नहीं है और मैं प्रतिबिंब का उपयोग करने से बचना चाहता हूं। यदि संभव हो तो स्वीकार करें, क्योंकि यह वर्तमान में (मेरे लिए) ऐसा करने का एकमात्र तरीका है।

/संपादित करें: मैं प्रतिनिधि घटना के लिए आवश्यक के हस्ताक्षर पता नहीं है, इस समस्या की मूल है

/संपादित करें 2: प्रतिनिधि contravariance एक अच्छी योजना की तरह लगता है हालांकि, मैं इस समाधान का उपयोग करने के लिए आवश्यक धारणा को

उत्तर

28

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

using System; 
using System.Linq; 
using System.Linq.Expressions; 
using System.Reflection; 

class ExampleEventArgs : EventArgs 
{ 
    public int IntArg {get; set;} 
} 

class EventRaiser 
{ 
    public event EventHandler SomethingHappened; 
    public event EventHandler<ExampleEventArgs> SomethingHappenedWithArg; 

    public void RaiseEvents() 
    { 
     if (SomethingHappened!=null) SomethingHappened(this, EventArgs.Empty); 

     if (SomethingHappenedWithArg!=null) 
     { 
      SomethingHappenedWithArg(this, new ExampleEventArgs{IntArg = 5}); 
     } 
    } 
} 

class Handler 
{ 
    public void HandleEvent() { Console.WriteLine("Handler.HandleEvent() called.");} 
    public void HandleEventWithArg(int arg) { Console.WriteLine("Arg: {0}",arg); } 
} 

static class EventProxy 
{ 
    //void delegates with no parameters 
    static public Delegate Create(EventInfo evt, Action d) 
    { 
     var handlerType = evt.EventHandlerType; 
     var eventParams = handlerType.GetMethod("Invoke").GetParameters(); 

     //lambda: (object x0, EventArgs x1) => d() 
     var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")); 
     var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke")); 
     var lambda = Expression.Lambda(body,parameters.ToArray()); 
     return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); 
    } 

    //void delegate with one parameter 
    static public Delegate Create<T>(EventInfo evt, Action<T> d) 
    { 
     var handlerType = evt.EventHandlerType; 
     var eventParams = handlerType.GetMethod("Invoke").GetParameters(); 

     //lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg) 
     var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray(); 
     var arg = getArgExpression(parameters[1], typeof(T)); 
     var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg); 
     var lambda = Expression.Lambda(body,parameters); 
     return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); 
    } 

    //returns an expression that represents an argument to be passed to the delegate 
    static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType) 
    { 
     if (eventArgs.Type==typeof(ExampleEventArgs) && handlerArgType==typeof(int)) 
     { 
      //"x1.IntArg" 
      var memberInfo = eventArgs.Type.GetMember("IntArg")[0]; 
      return Expression.MakeMemberAccess(eventArgs,memberInfo); 
     } 

     throw new NotSupportedException(eventArgs+"->"+handlerArgType); 
    } 
} 


static class Test 
{ 
    public static void Main() 
    { 
     var raiser = new EventRaiser(); 
     var handler = new Handler(); 

     //void delegate with no parameters 
     string eventName = "SomethingHappened"; 
     var eventinfo = raiser.GetType().GetEvent(eventName); 
     eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,handler.HandleEvent)); 

     //void delegate with one parameter 
     string eventName2 = "SomethingHappenedWithArg"; 
     var eventInfo2 = raiser.GetType().GetEvent(eventName2); 
     eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,handler.HandleEventWithArg)); 

     //or even just: 
     eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,()=>Console.WriteLine("!"))); 
     eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,i=>Console.WriteLine(i+"!"))); 

     raiser.RaiseEvents(); 
    } 
} 
+0

नरक, अभिव्यक्ति पेड़ बहुत अच्छे हैं। मैंने प्रतिबिंब के माध्यम से एक बार समान कोड लिखा था। प्रवेश करें। क्या मुसीबत है। –

+0

कोड का बड़ा टुकड़ा। क्या आप शायद तर्क दिखा सकते हैं कि तर्कों का समर्थन करने के लिए इसे कैसे बदला जाए? मैंने तर्कों के साथ विधि प्राप्त करने के लिए इसे बदल दिया, लेकिन मुझे "चर 'एक्स' प्रकार 'सिस्टम' स्ट्रिंग 'प्रकार से संदर्भित किया गया है, लेकिन यह परिभाषित नहीं किया गया है" जब मैं प्रतिनिधि बनाने की कोशिश करता हूं। धन्यवाद –

+0

हो गया- मैंने एक और उदाहरण जोड़ा। –

0

निर्भरता इंजेक्शन का उपयोग करके आप क्या हासिल कर सकते हैं। उदाहरण Microsoft Composite UI app block के लिए करता है आप वास्तव में क्या वर्णित

2

यह प्रतिबिंब

var o = new SomeObjectWithEvent; 
o.GetType().GetEvent("SomeEvent").AddEventHandler(...); 
उपयोग करके एक ईवेंट की सदस्यता के लिए संभव है

http://msdn.microsoft.com/en-us/library/system.reflection.eventinfo.addeventhandler.aspx

अब यहाँ समस्या यह है कि आप हल करने के लिए जा रहे हैं होने जा रहा है । प्रत्येक इवेंट हैंडलर के लिए आवश्यक प्रतिनिधियों के पास अलग-अलग हस्ताक्षर होंगे। आपको इन तरीकों को गतिशील रूप से बनाने के लिए दूर जाना होगा, जिसका अर्थ है प्रतिबिंब। स्वीकार करें, या आपको अपने स्वयं को एक निश्चित प्रतिनिधि को सीमित करना होगा ताकि आप इसे संकलित कोड से संभाल सकें।

उम्मीद है कि इससे मदद मिलती है। इस सदस्यता नहीं करता है

//reflect out the method to fire as a delegate 
EventHandler eventDelegate = 
    (EventHandler) Delegate.CreateDelegate(
     typeof(EventHandler), //type of event delegate 
     objectWithEventSubscriber, //instance of the object with the matching method 
     eventSubscriberMethodName, //the name of the method 
     true); 

, लेकिन विधि कॉल करने के लिए दे देंगे:

-1

आप की तरह कुछ मतलब है।

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

पोस्ट इस जवाब के बाद स्पष्ट किया गया है, मेरे उदाहरण है अगर आप प्रकार पता नहीं है मदद नहीं करेगा।

हालांकि नेट में सभी घटनाओं को डिफ़ॉल्ट ईवेंट पैटर्न का पालन करना चाहिए, इसलिए जब तक आप इसका पालन करते हैं तो यह मूल EventHandler के साथ काम करेगा।

1
public TestForm() 
{ 
    Button b = new Button(); 

    this.Controls.Add(b); 

    MethodInfo method = typeof(TestForm).GetMethod("Clickbutton", 
    BindingFlags.NonPublic | BindingFlags.Instance); 
    Type type = typeof(EventHandler); 

    Delegate handler = Delegate.CreateDelegate(type, this, method); 

    EventInfo eventInfo = cbo.GetType().GetEvent("Click"); 

    eventInfo.AddEventHandler(b, handler); 

} 

void Clickbutton(object sender, System.EventArgs e) 
{ 
    // Code here 
} 
8

यह एक पूरी तरह से सामान्य समाधान नहीं है, लेकिन अगर आपके सभी घटनाओं शून्य फू (वस्तु ओ, टी args), जहां टी EventArgs से निकला है के रूप में हैं, तो आप प्रतिनिधि contravariance का उपयोग दूर के साथ पाने के लिए कर सकते हैं यह। इस तरह (जहां KeyDown के हस्ताक्षर क्लिक करें की तरह ही नहीं है):

public Form1() 
    { 
     Button b = new Button(); 
     TextBox tb = new TextBox(); 

     this.Controls.Add(b); 
     this.Controls.Add(tb); 
     WireUp(b, "Click", "Clickbutton"); 
     WireUp(tb, "KeyDown", "Clickbutton"); 
    } 

    void WireUp(object o, string eventname, string methodname) 
    { 
     EventInfo ei = o.GetType().GetEvent(eventname); 

     MethodInfo mi = this.GetType().GetMethod(methodname, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); 

     Delegate del = Delegate.CreateDelegate(ei.EventHandlerType, this, mi); 

     ei.AddEventHandler(o, del); 

    } 
    void Clickbutton(object sender, System.EventArgs e) 
    { 
     MessageBox.Show("hello!"); 
    } 
2

LinFu प्रयास करें - यह एक सार्वभौमिक ईवेंट हैंडलर आप रनटाइम पर किसी भी घटना के लिए बाध्य करने देता है कि है।उदाहरण के लिए, यहाँ आप एक गतिशील बटन की घटना क्लिक करने के लिए कोई हैंडलर बाध्य कर सकते हैं आप है:

 
// Note: The CustomDelegate signature is defined as: 
// public delegate object CustomDelegate(params object[] args); 
CustomDelegate handler = delegate 
         { 
          Console.WriteLine("Button Clicked!"); 
          return null; 
         }; 

Button myButton = new Button(); 
// Connect the handler to the event 
EventBinder.BindToEvent("Click", myButton, handler); 

LinFu आप किसी भी घटना के लिए अपने संचालकों बाँध, प्रतिनिधि हस्ताक्षर की परवाह किए बिना सुविधा देता है। का आनंद लें!

आप इसे यहाँ पा सकते हैं: http://www.codeproject.com/KB/cs/LinFuPart3.aspx

1

मैं हाल ही में इकाई परीक्षण की घटनाओं का वर्णन ब्लॉग पोस्ट की एक श्रृंखला लिखा है, और तकनीकों मैं के बारे में बात की एक गतिशील घटना सदस्यता वर्णन करता है। मैंने गतिशील पहलुओं के लिए प्रतिबिंब और एमएसआईएल (कोड उत्सर्जन) का उपयोग किया, लेकिन यह सब अच्छी तरह से लपेटा गया है। DynamicEvent वर्ग का उपयोग करना, घटनाओं की सदस्यता ली जा सकती है गतिशील रूप से तो जैसे:

EventPublisher publisher = new EventPublisher(); 

foreach (EventInfo eventInfo in publisher.GetType().GetEvents()) 
{ 
    DynamicEvent.Subscribe(eventInfo, publisher, (sender, e, eventName) => 
    { 
     Console.WriteLine("Event raised: " + eventName); 
    }); 
} 

पैटर्न मैं कार्यान्वित की सुविधाओं में से एक था कि यह ईवेंट हैंडलर के लिए कॉल में घटना नाम injects तो तुम्हें पता घटना है जो उठाया गया इकाई परीक्षण के लिए बहुत उपयोगी है।

ब्लॉग आलेख काफी लंबा है क्योंकि यह एक ईवेंट यूनिट परीक्षण तकनीक का वर्णन कर रहा है, लेकिन पूर्ण स्रोत कोड और परीक्षण प्रदान किए जाते हैं, और आखिरी पोस्ट में गतिशील घटना सदस्यता लागू करने के बारे में एक विस्तृत विवरण दिया गया है।

http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/

+0

404 पर पास करें –

+0

@ रॉबर्ट चीयर्स टिप्पणी करने के लिए, हमारे पास हमारे वेब सर्वर को पैच करने के बाद कल कुछ आउटेज था। –

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