Open6
C#の非同期実装

メソッド内でイベント待ちするパターン
nugetから System.Reactive を追加しておく
using System.Reactive.Linq;
public partial class Form1 : Form
{
private async void button1_Click(object sender, EventArgs e)
{
var o = new MyController();
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
using (Observable
.FromEventPattern<MyEventArgs>(x => o.Received += x, x => o.Received -= x) // usingの最初にイベントをAdd、抜ける時にRemoveする
.Select(x => x.EventArgs) // イベントが呼び出された時に来る。イベントのMyEventArgsを取得
.Subscribe(x => tcs.SetResult(x.Data))) // TaskCompletionSourceにMyEventArgs.Dataの値を設定
{
o.FireEvent("test"); // イベントを発生させる
var data = await tcs.Task; // TaskCompletionSource.SetResult()が呼ばれるまで待つ
// data == "test"
}
}
}
class MyEventArgs : EventArgs
{
public MyEventArgs(string data) => this.Data = data;
public string Data { get; set; }
}
class MyController
{
public event EventHandler<MyEventArgs> Received;
public void FireEvent(string data) => this.Received.Invoke(this, new MyEventArgs(data));
}

await task; のタスク終了待ちでタイムアウトとかCancellationTokenを指定する方法
public static class TaskEx
{
// 戻り値無し
public static Task WaitAsync(this Task task, TimeSpan timeout) => WaitAsync(task, timeout, default);
public static async Task WaitAsync(this Task task, TimeSpan timeout, CancellationToken token)
{
var targetTask = await Task.WhenAny(new Task[]
{
task,
Task.Delay(timeout, token),
}
);
if (targetTask == task)
{
await task; // 普通にtaskが終了
}
else
{
token.ThrowIfCancellationRequested(); // キャンセルだったらThrow
throw new TimeoutException(); // でなければタイムアウト
}
}
// 戻り値有り
public static Task<T> WaitAsync<T>(this Task<T> task, TimeSpan timeout) => WaitAsync<T>(task, timeout, default);
public static async Task<T> WaitAsync<T>(this Task<T> task, TimeSpan timeout, CancellationToken token)
{
var targetTask = await Task.WhenAny(new Task[]
{
task,
Task.Delay(timeout, token),
}
);
if (targetTask == task)
{
return await task; // 普通にtaskが終了
}
else
{
token.ThrowIfCancellationRequested(); // キャンセルだったらThrow
throw new TimeoutException(); // でなければタイムアウト
}
}
}
使い方
// タイムアウト有り
var result = await task.WaitAsync(TimeSpan.FromSeconds(1000));
// タイムアウト+キャンセル有り
CancellationTokenSource cts = new CancellationTokenSource();
var result = await task.WaitAsync(TimeSpan.FromSeconds(1000), cts.Token);