使用EventArgs将信息传递回调用类

27

在事件处理程序中修改EventArgs以传递信息回调用事件的类是否被认为是不好的做法?

例如,如果我有一个低级通信类需要验证SSL证书,但它无法知道有效证书的外观,因为这是该类的不同用户所知道的。

class ValidationEventArgs : System.EventArgs
{
    public X509Certificate Certificate { get; set; }
    public bool Valid { get; set; }
}

然后在使用对象中,它们连接到事件,并以某种方式检查更改Valid标志以指示证书是否可接受。

comms.ValidationEvent += CertValidationHandler;
    
void CertValidationHandler(ValidationEventArgs args)
{
    if (args.Certificate.Issuer.Contains(COMPANY_NAME))
        args.Valid = true;
}
我已经看到过像这样使用 EventArgs参考文献,但我也看到有人说不建议这样做。 编辑:或许我应该澄清一下,这不是关于继承 EventArgs,而是使用它们作为双向通信的方式。如其他评论所述,这是可以接受的,任何对此持反对意见的言论都可能只是人们对概念的误解或滥用,就像现在有些人对 goto 一样。

6
谁说不推荐这样做呢?在框架中经常发生这种情况。看看 CancelEventArgs;没有比这更基础的了。 - Cody Gray
在网上搜索了一下,我有一种感觉,那就是拥有可变的EventArgs可能并不是应该做的事情,很可能是因为某个人在某个时候滥用了它。谢谢。 - r_ahlskog
像许多事物一样,它有适用的时间和地点,在错误的方式和地点使用它是不好的,但这并不意味着它本身不好。 - MikeT
3个回答

46

问问自己以下问题:“当我发布一个事件时,我是否希望任何订阅者改变任何EventArgs值?”如果答案是否定的,例如您正在广播只读信息,则使类成为不可变的;但是,如果您需要一些来自订阅者的反馈,则使需要更改的属性可变。

要澄清的是,在广播示例中,我们希望告诉任何订阅者某些事情,但不允许他们更改该值。

public class ProgressEventArgs : EventArgs
{
    public ProgressEventArgs(int current)
    {
        this.Current = current;
    }

    public int Current { get; private set; }
}

同样,我们也可以引发一个事件来请求类本身不知道的信息。

public class FeedbackEventArgs : EventArgs
{
    public bool ShouldContinue { get; set; }
    public string Reason { get; set; }
}

2
我知道这已经过时了,但我想指出,在使用此方法时应该小心,因为可能会有多个订阅者订阅同一个事件,但是传递的是相同的事件对象,因此使用简单的setter可能会导致数据被覆盖。 - Pharap
@Pharap 很好的观点!在可变示例中,这将是“最后一次写入获胜”的情况。 - Trevor Pilley

10
你可以通过泛型方法使用EventArgs类。 在本示例中,我将使用Rect类作为返回类型:

你可以通过通用类型方法使用EventArgs类。在这个样例中,我将使用Rect类作为返回类型:

public EventHandler<Rect> SizeRectChanged;

引发事件:

if(SizeRectChanged != null){
   Rect r = new Rect(0,0,0,0);
   SizeRectChanged(this,r);
}

监听事件:

anyElement.SizeRectChanged += OnSizeRectChanged;

public void OnSizeRectChanged(object sender, Rect e){
    //TODO abything using the Rect class
    e.Left = e.Top = e.Width = e.Height = 50;
}

1
这样做不起作用,因为EventHandler<TEventArgs>有where TEventArgs:EventArgs的限制。这意味着泛型类型必须派生自EventArgs。尝试使用Rect将导致编译器错误。 - BrandonLWhite
5
在 .NET 4.5 中取消了继承 EventArgs 的限制。 - Dashu

2

我不知道有没有建议不要继承EventArgs的情况;据我所知,从中继承是一个好的做法。

作为一个通用原则,我建议您使您派生的类不可变。这将使它更安全地在线程之间传递,如果需要的话。最简单的方法是将您的属性声明为{ get; private set; },并仅在构造函数中设置它们。显然,对于您在问题中提到的特定用例,您无法这样做,但应尽可能地这样做。


是的,我简化了这个例子,唯一可变的参数将是布尔值。问题是使用事件将数据传递回调用者,基本上作为双向通道,而不是继承EventArgs。我能看到的唯一问题是多个侦听器/线程,但在将其使用的规范中不允许这样做。 - r_ahlskog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接