【问题标题】:Implementing EventArgs Empty Correctly正确实现 EventArgs Empty
【发布时间】:2019-10-10 20:04:23
【问题描述】:

我正在尝试正确理解 Events 和 EventArgs,但无法完全了解整个 EventArgs.Empty 属性。

EventArgs 实现:

public static readonly EventArgs Empty;

并允许我们创建一个EventHandler 并使用以下方式调用它:

public event EventHandler<EventArgs> TestHappening;

private void MyMethod()
{
    TestHappening( this, EventArgs.Empty );
}

现在我研究了许多基于 EventArgs 的类,但似乎没有一个类实现了这一点,所以即使我已经阅读了我能找到的所有关于 EventArgs.Empty 的文档,我还是有点盲目。根据文档,“Empty 的值是 EventArgs 的只读实例,相当于调用 EventArgs 构造函数的结果”。

基于此,我创建了以下实现:

public class TestEventArgs : EventArgs
{
    public static readonly TestEventArgs Empty;

    public bool UpdatedValue { get; private set; }

    TestEventArgs()
        : this( false )
    {
    }

    public TestEventArgs( bool updatedValue )
    {
        this.UpdatedValue = updatedValue;
    }
}

public event EventHandler<TestEventArgs> TestHappening;

private void MyMethod()
{
    TestHappening( this, EventArgs.Empty );
}

使用TestEventArgs.Empty 是在实例化一个类还是它到底在做什么?

另外,即使我检查的所有子类都没有使用Empty,它们仍然可以使用它,暴露一个不可用的属性不是很混乱吗?

最后,根据我研究的各种文档,实际实例化EventArgs 的时间有两个主要差异。哪个被认为“更”正确?:

OnTestHappening( new TestEventArgs( false ) );

private void OnTestHappening( TestEventArgs e )
{
    var handler = TestHappening;

    if ( handler != null )
        handler( this, e );
}

对

OnTestHappening( true );

private void OnTestHappening( bool foo )
{
    var handler = TestHappening;

    if ( handler != null )
        handler( this, new TestEventArgs( foo ) );
}

【问题讨论】:

  • EventArgs 类被设计为一个基类。对于实际上具有有用属性的更专业的事件类。你不知道有一天你可能需要这样的课程。因此,您从 EventArgs 开始,可以非常轻松地更改它,而不会破坏任何事件处理程序。在那一天到来之前,您需要传递一个 EventArgs 实例。您可以使用 new EventArgs() 但这很浪费,EventArgs.Empty 是一个便宜的替代品。
  • 一开始就拥有这个类的“空”实例甚至没有意义。创建此类时,您应该期望人们在创建实例时实际提供布尔值,而不是使用默认值。
  • @Servy :我同意,我只是想完全理解它是如何工作的,并想如果我像以前那样把它分解,有一天它可能会帮助其他一些迷失的灵魂:)
  • @Storm 了解如何实现在您尝试实现它的上下文中没有意义的东西并不是教您如何正确使用该技术.如果你想学习如何使用一种技术,你应该在它是适合工作的工具的环境中使用它。

标签: c# events eventargs


【解决方案1】:

如果您真的需要 Empty 字段,您应该自己考虑。如果您不使用它,则不应创建它。 EventArgs 类没有任何变量或属性,因此每次都创建一个新实例没有意义。在你的情况下,因为你有一个默认值,所以有两个“空”TestEventArgs 更有意义,一个用于true,一个用于false(如果你真的想要并且它在你的场景中是有意义的)。

您在实现中遗漏了我在下面修复的其他一些要点:

public class TestEventArgs : EventArgs
{
    public static readonly TestEventArgs True = new TestEventArgs(true);

    public static readonly TestEventArgs False = new TestEventArgs(false);

    public bool UpdatedValue { get; private set; }

    public TestEventArgs(bool updatedValue)
    {
        this.UpdatedValue = updatedValue;
    }

    public event EventHandler<TestEventArgs> TestHappening;

    private void MyMethod()
    {
        EventHandler<TestEventArgs> eh = TestHappening;

        eh?.Invoke(this, TestEventArgs.True);
    }
}

我改变了什么:

  1. 将Empty 实例化为new TestEventArgs,因为这也是EventArgs.Empty 的定义。
  2. 我已经实现了事件处理程序的线程安全版本(代码中的第二个示例)。如果订阅事件的事件处理程序列表发生更改,则您的第一个示例不安全。您的第二个是,因此您应该选择该选项。

关于您的最后一点:这取决于您是否打算让调用代表更改对象(它传递一个实例与多个实例)。在您的情况下,由于实例无法更改,所以没那么重要。

【讨论】:

  • 好吧,现在说得很有道理,我就是想不通,我一定是想多了。关于线程安全,感谢您指出这一点,我仍在尝试将其放在膝盖下,为什么TestHappening( this, EventArgs.Empty ) 不是线程安全的,您是否有链接可以进一步解释它?我已经阅读了许多关于线程安全的文章,但似乎无法确定它
  • 真棒帕特里克,你能不能也回答我上面的第三个问题?
  • 为什么在readonly之前使用关键字new?我认为这是多余的,因为True 字段没有隐藏任何内容,还是我错了? eh(this, TestEventArgs.Empty); 行无法编译,因为 Empty 返回基本类型 EventArgs,它不能向下转换为 TestEventArgs。如果您隐藏Empty 字段而不是创建True 和False,您的示例将更有意义。除此之外,您可能还想从UpdateValue 中删除private set,因为它是只读的,并且可能将线程安全调用更新为TestHappening?.Invoke(this, TestEventArgs.True);
  • @fibriZoraZiel 你是对的。固定的。感谢您的评论。
猜你喜欢
  • 2019-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-18
  • 2010-10-05
  • 2010-10-15
相关资源
最近更新 更多