views:

113

answers:

2
+2  A: 

An event is basically a list of methods. To add to that list, first create a method which has the matching signature, then use += on the event field of the object which declares the event:

public class ChatBox
{
    public ChatBox(Messenger messenger)
    {
        messenger.Delivery += OnMessageDelivery;
    }

    private void OnMessageDelivery(object sender, MessageHandlerDeliveryEventArgs e)
    {
        if(e.Message.Type == MessageType.Chat)
        {
            Print(String.Format("{0}: {1}", e.DateTime, e.Message.Text));
        }
    }
}

The += wraps the method in a delegate and appends it to the existing list of delegates represented by Delivery. The handler is now associated with the event on that particular instance of Messenger.

Also, you don't need to use a custom delegate type. You can declare the event like this:

public event EventHandler<MessageHandlerDeliveryEventArgs> Delivery;

When someone is browing this style of API, they don't have to use Go To Definition to see what kind of EventArgs come with the event. You also have one less type to maintain and don't have to answer the question "does the delegate go in this file or a separate one?" (My answer is that it goes in a separate one; it is a type like any other and deserves its own artifact, even though you can write it in one line.)

Bryan Watts
So "public delegate void MessageDeliveryEvent(object sender, MessageHandlerDeliveryEventArgs e);" is synonymous with and redundant to "public event EventHandler<MessageHandlerDeliveryEventArgs> Delivery;"?
Superstringcheese
Yes. If you do a Go To Definition on `EventHandler<TEventArgs>`, you will see its signature is `public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)`.
Bryan Watts
+3  A: 

Here is an example of a typical convention followed for setting up standard .Net events:

using System;

namespace ObserverExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var subject = new Subject();
            var observer = new Observer();
            observer.Observe(subject);
            subject.SomeAction();
            Console.ReadLine();
        }
    }  

    public class Subject
    {
        public event EventHandler<TopicEventArgs> TopicHappening;

        public void SomeAction()
        {
            OnTopicHappening(new TopicEventArgs("Hello, observers!"));
        }

        protected virtual void OnTopicHappening(TopicEventArgs topicEventArgs)
        {
            EventHandler<TopicEventArgs> handler = TopicHappening;

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

    public class TopicEventArgs : EventArgs
    {
        public TopicEventArgs(string message)
        {
            Message = message;
        }

        public string Message { get; private set; }
    }

    public class Observer
    {
        public void Observe(Subject subject)
        {
            subject.TopicHappening += subject_TopicHappening;
        }

        void subject_TopicHappening(object sender, TopicEventArgs e)
        {
            Console.WriteLine(e.Message);
        }
    }
}


The three primary classes involved in this example are the Subject, Observer, and the TopicEventArgs. The Program class serves only to provide a driver for the example.

Looking first at the Program.Main() method, instances of Subject (the object that will be raising events) and Observer (the object that will be subscribing to the raised events) are first instantiated. Next, the observer is passed an instance of the subject allowing it the opportunity to subscribe to any desired events. Lastly, the subject's SomeAction() method is called which results in the raising of the event.

Looking at the Subject, we see that an event named TopicHappening of type EventHandler<TopicEventArgs> is publicly declared. The EventHandler type was introduced in .Net 2.0 and allows events to be declared without having to explicitly define delegate types. The Subject class also has two methods, SomeAction() and OnTopicHappening(). The method SomeAction() represents the point within the application where the Subject performs some task for which it wants to notify the world (i.e. "any observers") about. The method OnTopicHappening(TopicEventArgs) method provides the logical point within the class where the event will be raised. First, notice that it follows the naming convention On[Name of the event]. While this method can be named anything, this pattern has been widely adopted by convention. Second, notice that it is defined to take a single argument of type TopicEventArgs. This also follows a standard convention and serves the purpose of keeping the decision of what event arguments are raise at the logical point the event is raised (within the SomeAction() method) rather than from the physical point where the event is raised. Third, notice that it is declared protected virtual. This pattern is generally followed to allow any classes extending Subject to override what exactly happens when the TopicHappening event is raised. Within the OnTopicHappening() method, the TopicHappening event is assigned to a separate variable before the event is checked for null and invoked. This is to avoid a possible race condition where the event may be cleared by another thread (i.e. all observers unsubscribed) after the check for null, but before the event is invoked.

Looking at the TopicEventArgs class, this represents the event topic our subject raises. A custom EventArgs class is generally created when the subject needs to associate information with the event being raised. For subjects which only wish to send a signal event without any associated arguments, the base EventArgs.Empty class should be used.

Lastly, the Observer class defines the objects which will receive the event notifications from the Subject. In this example, the Observer class exposes an Observe() method just as a way to receive a reference to an instance of Subject. Within the method, a private event handler method named subject_TopicHappening is assigned to the TopicHappening event on the subject. This name format is the result of the delegate automatically generated by Visual Studio upon typing += when registering to handle the event. This essentially adds this method to the collection of methods to invoke when the event is raised by the subject. When invoked, the private subject_TopicHappening method simply writes the message contained within the event arguments out to the Console.

Hope this helps.

Derek Greer
+1 Nice description
Bryan Watts
This '*Happening' wording you use - is this common? This is the first example I've seen where it's phrased that way. Is there a more ubiquitous naming convention, or is this it? And thank you for all the work - this is exactly what I'm looking for, a complete example with no assumptions about my knowledge.
Superstringcheese
Events are used to denote that an action of interest is about to happen (pre-events) or has already happened (post-events). The suffixes "ing" and "ed" are common (e.g. Validating/Validated), but the plain present form of a verb is also often used when the post-event is implied (e.g. Button.Click). I chose the name "TopicHappening" in an attempt to keep things generic, but it isn't really the best example to follow. For example, Account.ReconciliationHappening and Account.ReconciliationHappened are awkward and unnecessarily verbose compared to Account.Reconciling and Account.Reconciled.
Derek Greer
Got it! This cleared things up for me, haha. I guess I was seeing it too literally. Now I'm more comfortable with what everything should be named.
Superstringcheese