使用新的異步/等待模型可以非常簡單地生成在事件觸發時完成的Task
;你只需要遵循這個模式:通用FromEvent方法
public class MyClass
{
public event Action OnCompletion;
}
public static Task FromEvent(MyClass obj)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
obj.OnCompletion +=() =>
{
tcs.SetResult(null);
};
return tcs.Task;
}
這允許:
await FromEvent(new MyClass());
的問題是,你需要爲每一個類每一個事件,你想await
創建一個新的FromEvent
方法上。這可能真的很快,而且主要是樣板代碼。
我非常希望能夠做這樣的事情:
await FromEvent(new MyClass().OnCompletion);
然後我可以重新使用相同的FromEvent
方法在任何情況下的任何事件。我花了一些時間嘗試創建這樣的方法,並且存在一些障礙。對於它上面的代碼將產生以下錯誤:
The event 'Namespace.MyClass.OnCompletion' can only appear on the left hand side of += or -=
據我所知,有將永遠不會成爲傳遞活動像這樣通過代碼的方式。
所以,退而求其次似乎想通過事件的字符串名字:
await FromEvent(new MyClass(), "OnCompletion");
它並不像理想;如果該類型的事件不存在,那麼您不會獲得智能感知,並會得到運行時錯誤,但它仍可能比大量的FromEvent方法更有用。
所以很容易使用反射和GetEvent(eventName)
來獲得EventInfo
對象。接下來的問題是該事件的委託在運行時不知道(並且需要能夠改變)。這使得難以添加事件處理程序,因爲我們需要在運行時動態創建一個方法,匹配給定的簽名(但忽略所有參數),這些簽名訪問我們已有的TaskCompletionSource
並設置其結果。
幸運的是,我找到了this link,其中包含有關如何通過Reflection.Emit
完成該操作的說明。現在問題是我們需要發射IL,並且我不知道如何訪問我擁有的tcs
實例。
下面是我對完成這個方面取得的進展:
public static Task FromEvent<T>(this T obj, string eventName)
{
var tcs = new TaskCompletionSource<object>();
var eventInfo = obj.GetType().GetEvent(eventName);
Type eventDelegate = eventInfo.EventHandlerType;
Type[] parameterTypes = GetDelegateParameterTypes(eventDelegate);
DynamicMethod handler = new DynamicMethod("unnamed", null, parameterTypes);
ILGenerator ilgen = handler.GetILGenerator();
//TODO ilgen.Emit calls go here
Delegate dEmitted = handler.CreateDelegate(eventDelegate);
eventInfo.AddEventHandler(obj, dEmitted);
return tcs.Task;
}
什麼IL我可不可以發出,讓我來設置TaskCompletionSource
的結果呢?或者,另外還有另一種方法來創建一個方法,該方法從任意類型中爲任意任意事件返回一個Task?
請注意,BCL具有'TaskFactory.FromAsync',可以輕鬆地從APM轉換爲TAP。沒有一種簡單的*和*通用的方式可以從EAP轉換爲TAP,所以我認爲這就是爲什麼MS不包括這樣的解決方案。我發現Rx(或TPL Dataflow)與「事件」語義更接近匹配 - 而Rx *也有* FromEvent'方法。 –
我也想做一個通用的'FromEvent <>',並且[this](http://stackoverflow.com/a/22798789/1768303)已經很接近了,因爲我可以在不使用反射的情況下實現這一點。 – Noseratio