CODEGATOR

.NET info worth sinking your teeth into!
Welcome to CODEGATOR Sign in | Join | Help
in Search

Martin Cook

Yet another C# developer with a blog.

An event broker solution - part 3

Last time I covered the EventPublisherModel class. Today I'll cover the EventSubscriberModel class, which is used to represent an event subscriber to the event broker. The code for the class looks like this:

internal sealed class EventSubscriberModel

{

 

    // Used to dispatch events to the UI's synchronization context.

    private static AsyncOperation asyncOperation =

        AsyncOperationManager.CreateOperation(null);

 

    // The owner of the model.

    private EventBroker owner;

 

    // The client's instance (if any).

    private WeakReference client;

 

    // The client's type.

    private Type clientType;

 

    // The client's event handler metadata.

    private MethodInfo methodInfo;

 

    // Used to dispatch events to the UI's synchronization context.

    private SendOrPostCallback synchronizedCallback;

 

    public EventSubscriberModel(

        EventBroker owner,

        object client,

        Type clientType,

        MethodInfo methodInfo,

        bool synchronize

        )

    {

 

        // Sanity check the arguments before using them.

        Guard.ThrowIfNull(owner, "owner");

        Guard.ThrowIfNull(clientType, "clientType");

        Guard.ThrowIfNull(methodInfo, "methodInfo");

 

        // Get the handler method's signature.

        ParameterInfo[] parms = methodInfo.GetParameters();

 

        // Are the number of parameters incorrect?

        if (parms.Length != 2)

            throw new TargetParameterCountException();

 

        // Is the first parameter the wrong type?

        if (!parms[0].ParameterType.IsAssignableFrom(typeof(object)))

            throw new ArgumentException(

                string.Format(

                    CultureInfo.CurrentCulture,

                    Resources.EventBrokerHandlerModel_TypeAssign,

                    "object"

                    ));

 

        // Is the second parameter the wrong type?

        if (!parms[1].ParameterType.IsAssignableFrom(typeof(EventArgs)))

            throw new ArgumentException(

                string.Format(

                    CultureInfo.CurrentCulture,

                    Resources.EventBrokerHandlerModel_TypeAssign,

                    "EventArgs"

                    ));

 

        // Save the fields.

        this.owner = owner;

        this.client = (client != null) ? new WeakReference(client) : null;

        this.clientType = clientType;

        this.methodInfo = methodInfo;

 

        // Should we create a synchronization delegate?

        if (synchronize)

            this.synchronizedCallback = new SendOrPostCallback(

                SynchronizedCallback

                );

 

    }

 

    public void FireMessage(object[] args)

    {

 

        // Should we forward the event directly or dispatch it to

        //   the UI's synchronization context?

 

        if (synchronizedCallback == null)

            methodInfo.Invoke(

                (client != null) ? client.Target : null,

                args

                );

        else

            asyncOperation.Post(

                synchronizedCallback,

                args

            );

    }

 

    public override bool Equals(

        object obj

        )

    {

 

        // Sanity check the argument before using it.

        Guard.ThrowIfNull(obj, "obj");

 

        // Is the object the wrong type?

        if (obj.GetType() != typeof(EventSubscriberModel))

            return false;

 

        // Recover a reference to the model.

        EventSubscriberModel model = (EventSubscriberModel)obj;

 

        // Compare the instances and return the results.

 

        if (model.client != null && client != null &&

            (model.client.Target != client.Target))

            return false;

 

        if (model.clientType != clientType)

            return false;

 

        if (model.methodInfo != methodInfo)

            return false;

 

        return true;

 

    }

 

    public override int GetHashCode()

    {

        return base.GetHashCode() +

            client.GetHashCode() +

            clientType.GetHashCode() +

            methodInfo.GetHashCode();

    }

 

    private void SynchronizedCallback(object state)

    {

        methodInfo.Invoke(

            (client != null) ? client.Target : null,

            (object[])state

            );

    }

}


Just like last time, I'll describe this class from top to bottom, starting with the fields and ending with the private methods. The first field is called asyncOperation, and is used to dispatch incoming events into the UI's synchronization context (If you don't know what a synchronization context is you can either Google for more information or click this link). The EventSubcriberModel class contains a back reference to the event broker for future use - it can be ignored for right now. The model maintains a reference to the actual subscriber object using a WeakReference, so that we don't interfere with the garbage collector's operation whenever a subscriber is destroyed after it is registered. The clientType field is what we'll use to reflect on the subscriber at runtime. The eventInfo field tells us everything we'll need to know about the event handler on the subscriber. The last field is called synchronizedCallback and is used to post incoming events back to the UI's synchronization context.

The constructor begins by validating the parameters (always a good idea) and then uses reflection to locate information about the subscribers event handler at runtime. It then performs some further validations on the subscribers event handler to ensure that the parameters conform to our expectations. Then it saves some of the field values for later use. Finally, if this subscriber has asked for synchronized publications then we create the callback for that purpose.

The FireMessage method is used to notify a subscriber about a published event. The event broker will call this method for every time a event is detected with an associated topic that matches the one for this subscriber. If the notification is synchronized then the subscriber's event handler is called indirectly through the SendOrPostCallback instance, if not then the event handler is called directly. This way, we only pay the overhead for synchronization when we actually need it.

The Equals and GetHashCode overrides are self-explanatory.

The SynchronizedCallback is the internal handler we use to synchronize the calling of the subscriber's event handler - for those subscribers who have asked for that feature.


OK, that's about all the time I have for blogging today. Tomorrow we'll look at the attribute classes that are used to associate a topic with a publisher/subscriber.

Comments

No Comments

Leave a Comment

(required) 
(optional)
(required) 
Submit

About Martin

I work as a software engineer specializing in designing and building object-oriented business solutions for Windows platforms using C#. I have been programming professionally for roughly 20 years.

This Blog

Syndication

Terms of Service | Privacy Statement