.net c#正确实现EventArgs为空

Implementing EventArgs Empty Correctly
2020-10-22
  •  译文(汉语)
  •  原文(英语)

我正在尝试适当地理解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 ) );
}
速聊1:
EventArgs类被设计为基类.对于实际上具有有用属性的更专门的事件类.你不知道什么时候你可能需要这样一类的某一天.因此,您从EventArgs开始,可以很容易地对其进行更改而不会破坏任何事件处理程序.在这一天之前,您需要传递一个EventArgs实例.您可以使用,new EventArgs()但这很浪费,EventArgs.Empty是一种廉价的选择.
速聊2:
首先,没有这个类的"空"实例甚至没有意义.创建此类时,您应该期望人们在创建实例时实际提供布尔值,而不是使用默认值.
速聊3:
:我同意,我只是想全面了解它的工作原理,并弄清楚是否像我一样分解它,有一天有可能会帮助其他迷路的人:)
速聊4:
了解如何在您试图实现的环境中实现没有意义的东西,并不是在教您如何正确使用该技术.如果您想学习如何使用一种技术,则应在适合该工具的情况下使用它.
解决过程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. 我已经实现了事件处理程序的线程安全版本(代码中的第二个示例).如果订阅该事件的事件处理程序列表发生了变化,那么您的第一个示例是不安全的.您的第二个是,因此您应该选择该选项.

关于最后一点:这取决于您是否打算让调用委托更改对象(它传递一个实例还是多个实例).在您的情况下,由于不能更改实例,所以没关系.

速聊1:
好的,这已经很有意义了,我只是无法做出这样的决定,我一定已经在考虑它了.关于线程安全,感谢您指出这一点,我仍在尝试使该TestHappening( this, EventArgs.Empty )线程屈膝,为什么线程不安全,您是否有进一步解释它的链接?我已经阅读了许多有关线程安全性的文章,但似乎无法将其牢记于心
速聊2:
很棒的帕特里克,您能否也回答我上面的第三个问题?
速聊3:
为什么new以前使用关键字readonly?我认为这是多余的,因为True字段没有隐藏任何内容,还是我错了? eh(this, TestEventArgs.Empty);该行将不会编译,因为它Empty返回基本类型EventArgs,该类型不能向下转换为TestEventArgs.如果您隐藏Empty字段而不是创建Trueand,那么您的示例将更有意义False.从Appart开始,您可能还希望将其删除private set,UpdateValue因为它是只读的,并且可能会将线程安全调用更新为TestHappening?.Invoke(this, TestEventArgs.True);
速聊4:
你是对的.固定.感谢您的评论.

I'm trying to properly understand Events and EventArgs but can't quite get a handle on the whole EventArgs.Empty property.

EventArgs implements:

public static readonly EventArgs Empty;

and allows us to create an EventHandler and call it using:

public event EventHandler<EventArgs> TestHappening;

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

Now I've studied a number of classes based on EventArgs and none of them seem to implement this so I'm flying a little blind even though I've read all the documentation I could find regarding EventArgs.Empty. According to the documentation, "The value of Empty is a read-only instance of EventArgs equivalent to the result of calling the EventArgs constructor".

Based on that I've created the following implementation:

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 );
}

Is the use of TestEventArgs.Empty instancing a class or what exactly is it doing?

Also, even though all the subclasses I checked didn't make use of Empty, they still have it available, isn't it confusing having an unusable property exposed?

Lastly, based on the various documents I studied, there were two main variances of when to actually instantiate the EventArgs. Which is considered "more" correct?:

OnTestHappening( new TestEventArgs( false ) );

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

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

vs

OnTestHappening( true );

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

    if ( handler != null )
        handler( this, new TestEventArgs( foo ) );
}
Talk1:
The EventArgs class is designed to be a base class. For more specialized event classes that actually have useful properties. You don't know when you may need such a class some day. So you start with EventArgs and can very easily change it without breaking any event handlers. Until that day comes, you need to pass an instance of EventArgs. You can use new EventArgs() but that's wasteful, EventArgs.Empty is a cheap alternative.
Talk2:
It doesn't even make sense to have an "empty" instance of this class in the first place. When you created this class you should expect people to actually provide a boolean when creating an instance, rather than using a default value.
Talk3:
: I agree, I'm just trying to fully understand how it works and figured if I broke it down like I did, it might help some other lost soul some day :)
Talk4:
Understanding how to implement something that doesn't make sense in the context you're trying to implement it in isn't teaching you how to use that technique properly. If you want to learn how to use a technique, you should be using it in a context where it's the appropriate tool for the job.
Solutions1

You should think for yourself if you really need the Empty field. If you don't instead to use it, you shouldn't create it. The EventArgs class doesn't have any variables or properties, so it doesn't make sense to create a new instance every time. In your case, since you have a default value, it does make more sense to have two 'empty' TestEventArgs, one for true and one for false (if you really want to and it makes sense in your scenario).

You are missing some other points in your implementation which I have fixed below:

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);
    }
}

What I have changed:

  1. Instantiated Empty as a new TestEventArgs, since that was the definition of EventArgs.Empty too.
  2. I have implemented the thread-safe version of the event handler (the second sample in your code). If the list of event handlers subscribed to the event changes, your first sample isn't safe. Your second is, and therefore you should that option.

Regarding your last point: it depends if you intend to have the calling delegates change the object (it is passing one instance vs multiple instances). In your case, since the instance can't be altered, it doesn't matter that much.

Talk1:
Ok, that makes a lot of sense now, I just couldn't make heads or tails of that, I must have been over thinking it. Regarding the thread safety, thanks for pointing that out, I'm still trying to actually get that one under the knee, why is TestHappening( this, EventArgs.Empty ) not thread safe and do you have a link to something that explains it further? I've read numerous write-ups on thread safety but just can't seem to get it nailed down
Talk2:
Awesome Patrick, could you also just answer my 3rd question above in your answer please?
Talk3:
Why do you use the keyword new before readonly? I think it's redundant because True field hides nothing, or am I wrong? eh(this, TestEventArgs.Empty); line won't compile because Empty returns the base type EventArgs, which cannot be downcasted to TestEventArgs. Your example would make more sense if you were hiding the Empty field instead of creating True and False. Appart from that, you might also want to remove private set from UpdateValue because it's readonly and maybe update the thread-safe invocation to TestHappening?.Invoke(this, TestEventArgs.True);
Talk4:
You are right. Fixed. Thanks for commenting.
转载于:https://stackoverflow.com/questions/30597904/implementing-eventargs-empty-correctly

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入