如何等待C#中的事件?

我正在创建一个包含一系列事件的类,其中一个是GameShuttingDown。当触发此事件时,我需要调用事件处理程序。该事件的目的是通知用户游戏正在关闭,他们需要保存其数据。可以等待保存,但不能保存事件。因此,当处理程序被调用时,游戏将在等待的处理程序完成之前关闭。

public event EventHandler<EventArgs> GameShuttingDown;

public virtual 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());

}

活动注册

// The game gets shut down before this completes because of the nature of how events work

DefaultGame.GameShuttingDown += async (sender, args) => await this.repo.Save(blah);

我知道事件的签名是正确的void

EventName,因此使它异步基本上是一发不可收拾。我的引擎大量使用事件通知第三者开发人员(和多个内部组件)事件正在引擎内发生,并让事件对事件做出反应。

有什么好的方法可以用我可以使用的基于异步的东西替换事件?我不确定是否应该使用回调BeginShutdownGameEndShutdownGame与之配合使用,但这很痛苦,因为只有调用源才能传递回调,而没有插入引擎的任何第三者材料,这就是我在事件中获得的东西。如果服务器调用game.ShutdownGame(),则引擎插件和引擎中的其他组件无法传递其回调,除非我采用某种注册方法,并保留了一系列回调。

任何关于采用哪种首选/推荐路线的建议都将不胜感激!我环顾四周,大部分情况下看到的是使用Begin / End方法,但我认为这不会满足我想要做的事情。

我正在考虑的另一个选择是使用注册方法,该方法需要等待回调。我遍历所有回调,获取它们的Task并等待一个WhenAll

private List<Func<Task>> ShutdownCallbacks = new List<Func<Task>>();

public void RegisterShutdownCallback(Func<Task> callback)

{

this.ShutdownCallbacks.Add(callback);

}

public async Task Shutdown()

{

var callbackTasks = new List<Task>();

foreach(var callback in this.ShutdownCallbacks)

{

callbackTasks.Add(callback());

}

await Task.WhenAll(callbackTasks);

}

回答:

就我个人而言,我认为拥有async事件处理程序可能不是最佳的设计选择,其中至少一个原因就是您所遇到的问题。使用同步处理程序,知道它们何时完成很简单。

就是说,如果由于某种原因您必须或至少被迫坚持这种设计,则可以以await友好的方式进行。

您注册处理程序和await他们的想法是一个好主意。但是,我建议您坚持使用现有的事件范例,因为这将使事件在代码中保持可表达性。最主要的是,您必须偏离EventHandler基于标准的委托类型,并使用返回a的委托类型,Task以便可以await使用处理程序。

这是一个简单的例子,说明我的意思:

class A

{

public event Func<object, EventArgs, Task> Shutdown;

public async Task OnShutdown()

{

Func<object, EventArgs, Task> handler = Shutdown;

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);

}

}

OnShutdown()在执行标准的“获取事件委托实例的本地副本”之后,该方法首先调用所有处理程序,然后等待所有返回的内容Tasks(在调用处理程序时将它们保存到本地数组中)。

这是一个简短的控制台程序,说明了用法:

class Program

{

static void Main(string[] args)

{

A a = new A();

a.Shutdown += Handler1;

a.Shutdown += Handler2;

a.Shutdown += Handler3;

a.OnShutdown().Wait();

}

static async Task Handler1(object sender, EventArgs e)

{

Console.WriteLine("Starting shutdown handler #1");

await Task.Delay(1000);

Console.WriteLine("Done with shutdown handler #1");

}

static async Task Handler2(object sender, EventArgs e)

{

Console.WriteLine("Starting shutdown handler #2");

await Task.Delay(5000);

Console.WriteLine("Done with shutdown handler #2");

}

static async Task Handler3(object sender, EventArgs e)

{

Console.WriteLine("Starting shutdown handler #3");

await Task.Delay(2000);

Console.WriteLine("Done with shutdown handler #3");

}

}

看完这个例子,我现在想知道C#是否没有办法抽象这一点。更改可能太复杂了,但是旧样式的void-returning事件处理程序和新的async/

await功能的当前组合确实有点尴尬。上面的方法可以正常工作(恕我直言,效果很好),但是对场景有更好的CLR和/或语言支持(即能够等待多播委托并将C#编译器将其转换为WhenAll())会很好。。

以上是 如何等待C#中的事件? 的全部内容, 来源链接: utcz.com/qa/425517.html

回到顶部