Ich erstelle eine Klasse, die eine Reihe von Ereignissen hat, eines davon ist GameShuttingDown
. Wenn dieses Ereignis ausgelöst wird, muss ich den Ereignishandler aufrufen. Der Zweck dieses Ereignisses besteht darin, Benutzer zu benachrichtigen, dass das Spiel heruntergefahren wird und sie ihre Daten speichern müssen. Die Speicherungen sind erwartbar und Ereignisse nicht. Wenn der Handler gerufen wird, wird das Spiel beendet, bevor die erwarteten Handler abgeschlossen werden können.
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());
}
Veranstaltungsanmeldung
// 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);
Ich verstehe, dass die Signatur für Ereignisse void EventName
so ist, dass es im Grunde genommen Feuer und Vergessen ist, sie asynchron zu machen. Meine Engine nutzt Eventing in hohem Maße, um Entwickler von Drittanbietern (und mehrere interne Komponenten) darüber zu informieren, dass Ereignisse in der Engine stattfinden, und sie auf diese reagieren zu lassen.
Gibt es einen guten Weg, um Eventing durch etwas Asynchrones zu ersetzen, das ich verwenden kann? Ich bin mir nicht sicher, ob ich Rückrufe verwenden BeginShutdownGame
und verwenden soll EndShutdownGame
, aber das ist ein Problem, da dann nur die anrufende Quelle einen Rückruf weiterleiten kann und keine Inhalte von Drittanbietern, die an die Engine angeschlossen werden, was ich bei Ereignissen bekomme . Wenn der Server anruft game.ShutdownGame()
, können Engine-Plugins und andere Komponenten innerhalb der Engine ihre Rückrufe nicht weiterleiten , es sei denn, ich verkabele eine Registrierungsmethode und behalte eine Sammlung von Rückrufen.
Jeder Rat, was die bevorzugte / empfohlene Route ist, wäre sehr dankbar! Ich habe mich umgesehen und zum größten Teil habe ich gesehen, dass ich den Anfang / Ende-Ansatz verwende, von dem ich nicht glaube, dass er das befriedigt, was ich tun möchte.
Bearbeiten
Eine andere Option, die ich in Betracht ziehe, ist die Verwendung einer Registrierungsmethode, die einen erwarteten Rückruf erfordert. Ich durchlaufe alle Rückrufe, greife nach ihrer Aufgabe und warte mit einem 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);
}