3

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 ) );
}
4
  • 1
    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. Commented Jun 2, 2015 at 13:51
  • 1
    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. Commented Jun 2, 2015 at 13:51
  • 1
    @Servy : 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 :) Commented Jun 2, 2015 at 14:10
  • 1
    @Storm 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. Commented Jun 2, 2015 at 14:11

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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
Awesome Patrick, could you also just answer my 3rd question above in your answer please?
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);
@fibriZoraZiel You are right. Fixed. Thanks for commenting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.