C# 非同期デリゲート: 非同期イベントを適切に待機します
C# では従来、イベントは void を返すため、非同期操作と互換性がありません。この問題は、イベント ハンドラーが他のコンポーネントに通知する前に非同期タスクを実行する必要がある場合に発生します。次のシナリオでは、この問題を調査し、非同期デリゲートを使用した解決策を提供します。
質問:
以下のコードは、ゲームが閉じられたときに発生する GameShuttingDown
という名前のイベントを示しています。各イベント ハンドラーは、ゲームを閉じる前にデータを非同期に保存する必要があります。ただし、ハンドラーは void メソッドで呼び出されるため、保存が完了する前にゲームが終了します。
<code class="language-csharp">public event EventHandler<EventArgs> GameShuttingDown; public async Task ShutdownGame() { await this.NotifyGameShuttingDown(); await this.SaveWorlds(); this.NotifyGameShutDown(); } private async Task SaveWorlds() { foreach (DefaultWorld world in this.Worlds) { await this.worldService.SaveWorld(world); } } protected virtual void NotifyGameShuttingDown() { var handler = this.GameShuttingDown; if (handler == null) { return; } handler(this, new EventArgs()); }</code>
解決策:
この問題を解決するには、タスクを返す非同期デリゲートを使用します。こうすることで、ハンドラーを非同期に呼び出して結果を待つことができます。
既存の GameShuttingDown
イベントを非同期デリゲート タイプに置き換えます:
<code class="language-csharp">public event Func<object, EventArgs, Task> GameShuttingDown;</code>
ハンドラーを呼び出して完了するまで待機するように NotifyGameShuttingDown
メソッドを変更します。
<code class="language-csharp">protected virtual async Task NotifyGameShuttingDown() { Func<object, EventArgs, Task> handler = GameShuttingDown; if (handler == null) { return; } Delegate[] invocationList = handler.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ((Func<object, EventArgs, Task>)invocationList[i])(this, EventArgs.Empty); } await Task.WhenAll(handlerTasks); }</code>
使用法:
新しい非同期デリゲート タイプを使用して GameShuttingDown
イベントをサブスクライブします:
<code class="language-csharp">DefaultGame.GameShuttingDown += async (sender, args) => await this.repo.Save(blah);</code>
このアプローチにより、イベント ハンドラー内のすべての非同期保存操作が完了した後にのみゲームが終了することが保証されます。
以上が非同期デリゲートを使用して C# で非同期イベントを処理する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。