Simon_Weaverの回答に基づいて、複数のサブスクライバーを処理でき、c#イベントと同様の構文を持つヘルパークラスを作成しました。
public class AsyncEvent<TEventArgs> where TEventArgs : EventArgs
{
private readonly List<Func<object, TEventArgs, Task>> invocationList;
private readonly object locker;
private AsyncEvent()
{
invocationList = new List<Func<object, TEventArgs, Task>>();
locker = new object();
}
public static AsyncEvent<TEventArgs> operator +(
AsyncEvent<TEventArgs> e, Func<object, TEventArgs, Task> callback)
{
if (callback == null) throw new NullReferenceException("callback is null");
//Note: Thread safety issue- if two threads register to the same event (on the first time, i.e when it is null)
//they could get a different instance, so whoever was first will be overridden.
//A solution for that would be to switch to a public constructor and use it, but then we'll 'lose' the similar syntax to c# events
if (e == null) e = new AsyncEvent<TEventArgs>();
lock (e.locker)
{
e.invocationList.Add(callback);
}
return e;
}
public static AsyncEvent<TEventArgs> operator -(
AsyncEvent<TEventArgs> e, Func<object, TEventArgs, Task> callback)
{
if (callback == null) throw new NullReferenceException("callback is null");
if (e == null) return null;
lock (e.locker)
{
e.invocationList.Remove(callback);
}
return e;
}
public async Task InvokeAsync(object sender, TEventArgs eventArgs)
{
List<Func<object, TEventArgs, Task>> tmpInvocationList;
lock (locker)
{
tmpInvocationList = new List<Func<object, TEventArgs, Task>>(invocationList);
}
foreach (var callback in tmpInvocationList)
{
//Assuming we want a serial invocation, for a parallel invocation we can use Task.WhenAll instead
await callback(sender, eventArgs);
}
}
}
これを使用するには、クラスで宣言します。次に例を示します。
public AsyncEvent<EventArgs> SearchRequest;
イベントハンドラーをサブスクライブするには、おなじみの構文を使用します(Simon_Weaverの回答と同じ)。
myViewModel.SearchRequest += async (s, e) =>
{
await SearchOrders();
};
イベントを呼び出すには、c#イベントに使用するのと同じパターンを使用します(InvokeAsyncでのみ)。
var eventTmp = SearchRequest;
if (eventTmp != null)
{
await eventTmp.InvokeAsync(sender, eventArgs);
}
c#6を使用している場合は、null条件演算子を使用して、代わりに次のように記述できるはずです。
await (SearchRequest?.InvokeAsync(sender, eventArgs) ?? Task.CompletedTask);