आप घटना के हस्ताक्षर से मेल खाने वाले गतिशील हैंडलर उत्पन्न करने के लिए System.Linq.Expressions.Expression
कक्षा का उपयोग कर सकते हैं - जिसमें आप बस Console.WriteLine
पर कॉल करते हैं।
Expression.Lambda
विधि (आपको आवश्यक विशिष्ट अधिभार के लिए एक लिंक प्रदान किया गया है) Func<>
उत्पन्न करने के लिए या अधिक संभावना है, Action<>
सही प्रकार के।
आपको ईवेंट के प्रतिनिधि प्रकार प्रतिबिंबित (हथियाने यह Invoke
पद्धति के रूप में @Davio ने उल्लेख किया है) सभी तर्कों बाहर निकालते हैं और लैम्ब्डा विधि के लिए आपूर्ति करने के लिए उन लोगों में से प्रत्येक के लिए ParameterExpression
रों बनाने के लिए।
यहाँ एक पूर्ण समाधान है कि आप एक मानक इकाई परीक्षण में चिपका सकते हैं, मैं एक अनुवर्ती संपादन में बाद में समझाने होगी:
public class TestWithEvents
{
//just using random delegate signatures here
public event Action Handler1;
public event Action<int, string> Handler2;
public void RaiseEvents(){
if(Handler1 != null)
Handler1();
if(Handler2 != null)
Handler2(0, "hello world");
}
}
public static class DynamicEventBinder
{
public static Delegate GetHandler(System.Reflection.EventInfo ev) {
string name = ev.Name;
// create an array of ParameterExpressions
// to pass to the Expression.Lambda method so we generate
// a handler method with the correct signature.
var parameters = ev.EventHandlerType.GetMethod("Invoke").GetParameters().
Select((p, i) => Expression.Parameter(p.ParameterType, "p" + i)).ToArray();
// this and the Compile() can be turned into a one-liner, I'm just
// splitting it here so you can see the lambda code in the Console
// Note that we use the Event's type for the lambda, so it's tightly bound
// to that event.
var lambda = Expression.Lambda(ev.EventHandlerType,
Expression.Call(typeof(Console).GetMethod(
"WriteLine",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(string) },
null), Expression.Constant(name + " was fired!")), parameters);
//spit the lambda out (for bragging rights)
Console.WriteLine(
"Compiling dynamic lambda {0} for event \"{1}\"", lambda, name);
return lambda.Compile();
}
//note - an unsubscribe might be handy - which would mean
//caching all the events that were subscribed for this object
//and the handler. Probably makes more sense to turn this type
//into an instance type that binds to a single object...
public static void SubscribeAllEvents(object o){
foreach(var e in o.GetType().GetEvents())
{
e.AddEventHandler(o, GetHandler(e));
}
}
}
[TestMethod]
public void TestSubscribe()
{
TestWithEvents testObj = new TestWithEvents();
DynamicEventBinder.SubscribeAllEvents(testObj);
Console.WriteLine("Raising events...");
testObj.RaiseEvents();
//check the console output
}
एक रूपरेखा - हम एक प्रकार कुछ घटनाओं है कि के साथ शुरू (मैं Action
का उपयोग कर रहा हूं लेकिन इसे किसी भी चीज़ के साथ काम करना चाहिए), और इसमें एक तरीका है जिसका उपयोग हम उन सभी घटनाओं का परीक्षण करने के लिए कर सकते हैं जिनके पास ग्राहक हैं।
फिर DynamicEventBinder
कक्षा में, जिसमें दो विधियां हैं: GetHandler
- किसी विशेष प्रकार के लिए किसी विशेष ईवेंट के लिए हैंडलर प्राप्त करने के लिए; और जो उन सभी घटनाओं को उस प्रकार के किसी दिए गए उदाहरण के लिए बाध्य करता है - जो सभी घटनाओं पर बस लूप करता है, AddEventHandler
प्रत्येक के लिए, GetHandler
को हैंडलर प्राप्त करने के लिए बुलाता है।
GetHandler
विधि वह जगह है जहां मांस और हड्डियां हैं - और जैसा कि मैंने रूपरेखा में सुझाव दिया है।
एक प्रतिनिधि प्रकार में Invoke
सदस्य संकलक द्वारा संकलित किया गया है जो किसी भी हैंडलर के हस्ताक्षर को प्रतिबिंबित करता है जो इसे बाध्य किया जा सकता है। इसलिए, हम उस विधि को प्रतिबिंबित करते हैं और इसके लिए कोई भी पैरामीटर प्राप्त करते हैं, प्रत्येक के लिए लिंक ParameterExpression
उदाहरण बनाते हैं। पैरामीटर नामकरण एक अच्छी तरह से है, यह यहां मायने रखता है।
फिर हम एक एकल लाइन लैम्ब्डा, जिनके शरीर मूल रूप से है निर्माण:
Console.WriteLine("[event_name] was fired!");
(यहाँ ध्यान दें कि ईवेंट के नाम कोड के रूप में जहाँ तक गतिशील कोड में खींच लिया और एक निरंतर स्ट्रिंग में शामिल किया जाता है चिंतित)
जब हम लैम्ब्डा का निर्माण, हम भी Expression.Lambda
विधि प्रतिनिधि के प्रकार हम बनाने का इरादा (घटना के प्रतिनिधि प्रकार) के लिए सीधे बाध्य बताओ, और है कि हम से पहले बनाए गए ParameterExpression
सरणी पारित करके है , यह एक विधि उत्पन्न करेगा जिसमें टी है टोपी कई मानकों। हम गतिशील कोड को वास्तव में संकलित करने के लिए Compile
विधि का उपयोग करते हैं, जो हमें Delegate
देता है जिसे हम AddEventHandler
पर तर्क के रूप में उपयोग कर सकते हैं।
मैं ईमानदारी से आशा करता हूं कि यह बताता है कि हमने क्या किया है - यदि आपने अभिव्यक्तियों और गतिशील कोड के साथ काम नहीं किया है, इससे पहले कि यह दिमागी झुकाव हो। दरअसल, जिन लोगों के साथ मैं काम करता हूं उनमें से कुछ बस इस वूडू को बुलाते हैं। उदाहरण को
यह दिलचस्प लगता है लेकिन मैं लागू करने के लिए संघर्ष कर रहा हूं! – Nick
मैं आपके साथ उपयोग करने के लिए टाइप + और ईवेंट के साथ एक टेस्ट रखूंगा ... आधा घंटे लग सकता है ... –
मुझे उस भाग में जाना है जहां मैं EventInfo ऑब्जेक्ट पर AddHandler को कॉल करता हूं और मुझे मिल रहा है: * "ऑब्जेक्ट ऑफ टाइप 'System.Action'2 [System.Object, System.Windows.Input.MouseButtonEventArgs]' को 'System.Windows.Input.MouseButtonEventHandler' प्रकार में परिवर्तित नहीं किया जा सकता है।" * – Nick