您可以使用System.Linq.Expressions.Expression
類來生成與事件簽名匹配的動態處理程序 - 只需撥打電話Console.WriteLine
即可。
可以使用Expression.Lambda
方法(提供了您需要的特定超載的鏈接)來生成正確類型的Func<>
或更可能的Action<>
。
您反映事件的委託類型(抓取它的@Davio提到的Invoke
方法)以提取所有參數併爲要提供給lambda方法的每個參數創建。
這裏,你可以粘貼到一個標準的單元測試一個完整的解決方案,我將在後續的編輯事後解釋:
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
成員編譯到它的編譯器,它反映了它可以綁定到任何處理程序的簽名。因此,我們反映該方法並獲取它的任何參數,從而爲每個實例創建Linq實例。命名參數是一個很好的選擇,這裏的類型很重要。
然後我們建立了一個單行的λ,它的身體基本上是:
Console.WriteLine("[event_name] was fired!");
(這裏注意事件的名字就被拉入動態代碼,並納入一個常量字符串作爲密碼而言)
當我們建立拉姆達,我們還告訴Expression.Lambda
方法委託的類型,我們打算建立(直接鍵合於委託類型的事件),並且通過使我們之前創建的陣列,它會產生一個有t的方法帽子很多參數。我們使用Compile
方法來實際編譯動態代碼,它給了我們一個Delegate
,然後我們可以將它用作參數AddEventHandler
。
我真誠地希望這解釋了我們所做的 - 如果您在表達式和動態代碼之前沒有使用過表達式和動態代碼,那麼它可能是令人頭痛的事情。事實上,我一起工作的一些人簡單地稱之爲巫術。
這聽起來很有趣,但我很努力實施! – Nick
我會把類型+和事件的測試放在一起給你使用......可能需要半個小時...... –
我已經到了我在EventInfo對象上調用AddHandler的部分,並且我得到: *'System.Action'2類型的對象無法轉換爲'System.Windows.Input.MouseButtonEventHandler'類型。「* – Nick