【问题标题】:System.Action<T> as EventHandler [duplicate]System.Action<T> 作为 EventHandler [重复]
【发布时间】:2011-05-24 09:00:31
【问题描述】:

反对使用代理 System.ActionSystem.Func 作为 EventDelegates 而不是经典的 EventHandler 模式。我会因此遇到问题吗?

private bool disposed;

public event Action<IUnitOfWork, IContext> Disposing;

public void Dispose()
{
    if (this.disposed)
    {
        return;
    }

    if (null != this.Disposing)
    {
        this.Disposing(this, this.AttachedContext);
    }

    this.disposed = true;
}

用法:

unitOfWorkInstance.Disposing += (u, c) => c.Rollback(u); // in my opinion more readable than
unitOfWorkInstance.Disposing += (sender, args) => args.AttachedContext.Rollback(sender as IUnitOfWork);

【问题讨论】:

  • Dispose 方法 [...] 应该可以多次调用而不会引发异常。 msdn.microsoft.com/en-us/library/b1yfkh5e(v=VS.100).aspx
  • 非常感谢您的提示。在这种情况下,我将异常替换为简单的返回。
  • 虽然这是有创意的 (+1),但我看不出它解决了什么问题。一方面,disposingInternal 的定义不是仍将是代表吗...您(故意?)将其从代码 sn-p 中删除,所以也许您会让我感到惊讶

标签: c# .net action event-handling func


【解决方案1】:

好吧,您提供的代码不是线程安全的 - 在您的无效性测试之后和您致电 this.Disposing 之前,有人可能会取消订阅事件处理程序。

但总的来说,它应该可以正常工作。不利的一面是,如果不遵循 EventHandler 约定,您可以订阅的内容会受到一些限制。

例如,假设你有一个非常通用的事件处理方法:

public void LogEvent(object sender, EventArgs e)
{
    Console.WriteLine("Event raised");
}

您可以使用它来按照正常约定订阅 any 事件 - 但不能使用 你的活动。

不过,这是一个很小的缺点。我想一个可能更大的问题是它可能会使其他期望看到常规事件签名的开发人员感到困惑。

编辑:我刚刚记得其他一些库可能期望传统的事件签名 - 例如,Reactive Extensions 就可以。 IIRC,订阅其他事件也不是不可能,只是有点难。

【讨论】:

  • 同意你所说的,“虽然这是一个很小的缺点” - 根据他们拥有多少控制权,他们可以使用 lambda 进行调整:unitOfWork.Disposing += (uow, context) =&gt; LogEvent(uow, new EventArgs());
  • FWIW:我通常虔诚地避免使用object sender, EventArgs e。也就是说,除非我真的需要多播语义,否则我倾向于避免使用事件委托。我知道“标准签名”的灵活性是什么,但 90% 的时间是学术上的好处,但会降低代码的可读性。在框架设计中,我会保证标准事件签名,尽管如此
  • +1 用于提及响应式扩展:D
【解决方案2】:

从“代码工作”的角度来看,我认为将这些委托类型用于事件是完全可以的。

这样做的问题在于,您没有遵循事件的通用模式,其中委托是 EventHandler&lt;TEventArgs&gt;,而 TEventArgs 是包含事件参数的自定义类型。遵循这种模式的好处包括:

  • 代码可读性
  • 如果您需要向事件添加参数,则不必更改事件订阅者(因为您只需将其添加到自定义事件参数类中)。

【讨论】:

    【解决方案3】:

    一般:

    使用Action 作为您的事件处理程序没有问题。它受语言支持,所以使用它:)

    我能想到的唯一情况是尝试通过反射找到您的事件的代码。但如果该代码无法将任何委托作为事件类型处理,我会说他们的代码有问题,而不是你的。

    你的具体例子:

    您正在使用的模式的问题是您不应该在 Dispose 方法中真正使用该对象。有时它可能是安全的,但很容易出错。

    例如,如果Dispose 方法在引发事件之前释放了资源,则该对象将处于不可用状态。

    维护程序员在编辑您的 Dispose 方法时可能很难做到正确(没有 cmets 和强大的代码审查)。

    【讨论】:

    • 我的这个 UnitOfWork 实现的唯一公共成员是:Commit()/Rollback()/Dispose() and Disposing。 Disposing 事件只会通知其他对象(Transation/DataContext)。在我的课堂上,我没有任何资源管理。
    • @ben:异常安全是此场景中高优先级关注的另一个示例。向Dispose 方法添加事件将使确保异常安全(包括双掷)变得更加困难。除非您有充分的理由来证明增加的复杂性是合理的,否则我认为您可能会自取其辱。但我的建议可以随心所欲地接受或离开:)
    • @Ben dotnet:维护程序员的观点暗示我们也在讨论这段代码在未来几年的外观,在未来的版本中,而不仅仅是你当前的实现包含的内容。 (根据我的经验,从现在开始的 2 个月后,您已经有一段时间没有查看代码,并且必须回来修复错误)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-16
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    相关资源
    最近更新 更多