gpt4 book ai didi

c# - 具有有状态事件参数的异步事件处理程序

转载 作者:行者123 更新时间:2023-11-30 23:29:34 25 4
gpt4 key购买 nike

有时我们希望事件发布者在触发后的操作取决于事件处理程序设置的某些状态。一个常见的示例是采用 CancelEventArgs 的事件。事件发布者在触发后采取行动,条件是 Cancel 属性的值。如果处理程序长时间运行,我们不想阻塞 UI 线程(用户可以在等待时做其他事情)。但是异步处理程序可能会在设置状态之前返回,并且发布者在需要时不会获得正确的值。我通过简单地在事件参数中提供一个可变的 Task 属性来解决这个问题。处理程序有责任设置它的值,发布者有责任在触发后等待它。这似乎工作正常。

一个反对意见可能是有状态的事件参数可以说是不好的做法,除非你假设只有一个处理程序。您可以使用高阶函数而不是事件,这将通过强制执行一个“处理程序”来处理上述异议。

我不确定的一件事是 async/await 如何处理多个等待者。

  • 是否可以保证延续的执行顺序?
  • 是否会出现竞争条件,即其余的触发可能先于事件处理程序的其余部分执行?

答案是yes .对于嵌套的异步方法来说,这感觉通常会成为一个问题,只要调用者和被调用者在等待之后都有操作。

  • 我的做法有没有什么缺点我没有提到?
  • 是否有一些其他众所周知或广为接受的最佳实践来实现我不知道的目标?
  • 关于如何处理竞争条件的任何想法?

谢谢!

class Publisher
{
void RaiseMyEvent()
{
var e = new MyEventArgs();
OnRaiseMyEvent(e);
if (e.Task != null) await e.HandlerTask;
if (e.Cancel)
{
// Do one thing
}
else
{
// Do the other
}
}
}

class Subscriber
{
void MyEventHandler(object sender, CancelEventArgs e)
{
// Notify user to wait on process
e.Task = SomeAsyncMethod();
await e.Task;
e.Cancel = GetOutcome();
// Clear any notification
}

bool GetOutcome() { }
}

实际上,我们可以通过确保触发方法所需的事件参数中的任何所需状态值在处理程序中继续之前设置来避免竞争:

class Subscriber 
{
void MyEventHandler(object sender, CancelEventArgs e)
{
// Notify user to wait on process
e.Task = Task.Run(() =>
{
//Do stuff
e.Cancel = GetOutcome();
}
await e.Task;
// Clear any notification
}

bool GetOutcome() { }
}

两个延续都在 UI 线程上执行,但我们不关心顺序。

最佳答案

Sometimes we want the event publisher's actions post firing to depend on some state set by the event handlers.

我将这些称为“命令事件”,而不是“通知事件”,并涵盖 a few approaches to async command events on my blog .

经过相当多的经验,我得出的结论是命令事件是一种反模式。 .NET 中的事件被设计为通知事件,让它们的行为有所不同充其量也很尴尬。使用 GoF术语,.NET 事件用于实现Observer 设计模式,但这些“命令事件”实际上是Template Method 设计模式的实现。

考虑一下关于模板方法设计模式的引述(第 328 页):

It's important for template methods to specify which operations are hooks (may be overridden) and which are abstract operations (must be overridden)

这是一个很好的命令事件识别质量!如果您发现自己编写的 .NET 事件需要一个处理程序或不能有多个处理程序,那么这很好地表明 .NET 事件可能是错误的解决方案。

如果您有模板方法的情况,那么通常某种形式的解决方案就足够了:

interface IDetails { Task ProcessAsync(); }
class Subject
{
private IDetails _details { get; }
public Subject(IDetails details) { _details = details; }
async Task SomeMethodAsync()
{
...
if (_details)
await _details.ProcessAsync();
}
}

关于c# - 具有有状态事件参数的异步事件处理程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35327897/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com