Understanding events and event handlers in C#

2018-12-31 08:18发布

I understand the purpose of events, especially within the context of creating user interfaces. I think this is the prototype for creating an event:

public void EventName(object sender, EventArgs e);

What do event handlers do, why are they needed, and how do I to create one?

9条回答
琉璃瓶的回忆
2楼-- · 2018-12-31 08:39

I agree with KE50 except that I view the 'event' keyword as an alias for 'ActionCollection' since the event holds a collection of actions to be performed (ie. the delegate).

using System;

namespace test{

class MyTestApp{
    //The Event Handler declaration
    public delegate void EventAction();

    //The Event Action Collection 
    //Equivalent to 
    //  public List<EventAction> EventActions=new List<EventAction>();
    //        
    public event EventAction EventActions;

    //An Action
    public void Hello(){
        Console.WriteLine("Hello World of events!");
    }
    //Another Action
    public void Goodbye(){
        Console.WriteLine("Goodbye Cruel World of events!");
    }

    public static void Main(){
        MyTestApp TestApp = new MyTestApp();

        //Add actions to the collection
        TestApp.EventActions += TestApp.Hello;
        TestApp.EventActions += TestApp.Goodbye;

        //Invoke all event actions
        if (TestApp.EventActions!= null){
            //this peculiar syntax hides the invoke 
            TestApp.EventActions();
            //using the 'ActionCollection' idea:
            // foreach(EventAction action in TestApp.EventActions)
            //     action.Invoke();
        }
    }

}   

}
查看更多
流年柔荑漫光年
3楼-- · 2018-12-31 08:40

That is actually the declaration for an event handler - a method that will get called when an event is fired. To create an event, you'd write something like this:

public class Foo
{
    public event EventHandler MyEvent;
}

And then you can subscribe to the event like this:

Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);

With OnMyEvent() defined like this:

private void OnMyEvent(object sender, EventArgs e)
{
    MessageBox.Show("MyEvent fired!");
}

Whenever Foo fires off MyEvent, then your OnMyEvent handler will be called.

You don't always have to use an instance of EventArgs as the second parameter. If you want to include additional information, you can use a class derived from EventArgs (EventArgs is the base by convention). For example, if you look at some of the events defined on Control in WinForms, or FrameworkElement in WPF, you can see examples of events that pass additional information to the event handlers.

查看更多
低头抚发
4楼-- · 2018-12-31 08:40
//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;

//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);
查看更多
妖精总统
5楼-- · 2018-12-31 08:47

To understand event handlers, you need to understand delegates. In C#, you can think of a delegate as a pointer (or a reference) to a method. This is useful because the pointer can be passed around as a value.

The central concept of a delegate is its signature, or shape. That is (1) the return type and (2) the input arguments. For example, if we create a delegate void MyDelegate(object sender, EventArgs e), it can only point to methods which return void, and take an object and EventArgs. Kind of like a square hole and a square peg. So we say these methods have the same signature, or shape, as the delegate.

So knowing how to create a reference to a method, let's think about the purpose of events: we want to cause some code to be executed when something happens elsewhere in the system - or "handle the event". To do this, we create specific methods for the code we want to be executed. The glue between the event and the methods to be executed are the delegates. The event must internally store a "list" of pointers to the methods to call when the event is raised.* Of course, to be able to call a method, we need to know what arguments to pass to it! We use the delegate as the "contract" between the event and all the specific methods that will be called.

So the default EventHandler (and many like it) represents a specific shape of method (again, void/object-EventArgs). When you declare an event, you are saying which shape of method (EventHandler) that event will invoke, by specifying a delegate:

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;

//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

//To raise the event within a method.
SomethingHappened("bar");

(*This is the key to events in .NET and peels away the "magic" - an event is really, under the covers, just a list of methods of the same "shape". The list is stored where the event lives. When the event is "raised", it's really just "go through this list of methods and call each one, using these values as the parameters". Assigning an event handler is just a prettier, easier way of adding your method to this list of methods to be called).

查看更多
只若初见
6楼-- · 2018-12-31 08:48

Just to add to the existing great answers here - building on the code in the accepted one, which uses a delegate void MyEventHandler(string foo)...

Because the compiler knows the delegate type of the SomethingHappened event, this:

myObj.SomethingHappened += HandleSomethingHappened;

Is totally equivalent to:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

And handlers can also be unregistered with -= like this:

// -= removes the handler from the event's list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;

For completeness' sake, raising the event can be done like this, only in the class that owns the event:

//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
    handler("Hi there!");
}

The thread-local copy of the handler is needed to make sure the invocation is thread-safe - otherwise a thread could go and unregister the last handler for the event immediately after we checked if it was null, and we would have a "fun" NullReferenceException there.


C# 6 introduced a nice short hand for this pattern. It uses the null propagation operator.

SomethingHappened?.Invoke("Hi there!");
查看更多
浪荡孟婆
7楼-- · 2018-12-31 08:56

publisher: where the events happen. Publisher should specify which delegate the class is using and generate necessary arguments, pass those arguments and itself to the delegate.

subscriber: where the response happen. Subscriber should specify methods to respond to events. These methods should take the same type of arguments as the delegate. Subscriber then add this method to publisher's delegate.

Therefore, when the event happen in publisher, delegate will receive some event arguments (data, etc), but publisher has no idea what will happen with all these data. Subscribers can create methods in their own class to respond to events in publisher's class, so that subscribers can respond to publisher's events.

查看更多
登录 后发表回答