简体   繁体   中英

Simplest way to handle creating events in a class

While playing around in VS 2015, i was creating a property and mistyped prior to hitting tab and it gave me what appears to be a very clean simple method for creating an event. I am unsure how to use this to accept additional parameters to pass it data (as I usually want to pass data when raising an event with information about what is in progress, or what was done)

The code it gave is as follows :

public event EventHandler<EventArgs> MyEvent;
protected virtual void OnMyEvent(EventArgs e)
{
    EventHandler<EventArgs> handler = MyEvent;
    if (handler != null)
        handler(this, e);
}

At first glance it is similar to most examples however this method does not appear to require a delegate to operate. Compare with the following example from How to call an event manually in C#?

public delegate void MyEventHandler(object sender, MyEventArgs e)
public event MyEventHandler MyEvent;
public void RaisesMyEvent()
{
   if(MyEvent != null) //required in C# to ensure a handler is attached
      MyEvent(this, new MyEventArgs(/*any info you want handlers to have*/));
}

What I would like to know, in light of having more elegant (clean) code, is the method that microsoft provided (first method in this question) better (it seems cleaner), and if so, how could I adjust it slightly to accept a string in addition to standard event args and/or extend the eventargs to pass information ?

The code that Visual Studio auto-generated for you is actually better for one small condition:

public void RaisesMyEvent()
{
    if(MyEvent != null) //required in C# to ensure a handler is attached
        // Now if someone in another thread sets MyEvent to null the next line will still execute
        MyEvent(this, new MyEventArgs(/*any info you want handlers to have*/));
        // And throw a NullReferenceException
}

This code has a race condition. It's fairly rare to hit it, but it is possible. The Visual Studio boilerplate code avoids this race condition by making a local copy of the event handler before calling it.

As for how to pass more parameters to the event handler:

There are multiple ways, but the 'standard' way is to create an class that extends EventArgs. For instance, PaintEventArgs extends EventArgs and adds a Graphics and Rectangle properties for the OnPaint handler to use.

I'd follow that basic pattern by creating a MyEventArgs class based off EventArgs , and adding the properties your method will need to use.

To pass custom arguments on an EventHandler , you would create your own class derived from EventArgs and change the declaration like so:

public event EventHandler<MyCustomEventArgs> MyEvent;

Alternatively, if you don't like having object sender in your handlers, you can do:

public event Action<MyCustomEventArgs> MyEvent;

or even

public event Action<string> MyEvent;

As far as the invocation, Microsoft's is better. The reason is that the null check you wrote isn't thread-safe. Storing off the handler before doing the null check ensures that you don't invoke a null method.

Alternatively, use C# 6 invocations and take advantage of the null conditional operator:

MyEvent?.Invoke(whateverArgs);

Just replace EventArgs with your own class:

class MyEventArgs : EventArgs
{
    public string Text { get; private set; }

    public MyEventArgs(string text)
    {
        Text = text;
    }
}

The EventHandler<T> generic delegate type will accept EventArgs or any subclass of EventArgs .

Then when you call OnMyEvent() , just pass an instance of your class with the data you want to deliver with the event.

I agree with the other notes regarding thread-safe invocation. This isn't always required, but it's simple enough to implement and the IDE-provided version is safe while yours is not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM