Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>This is how I've done this in C#. </p> <p>I think this approach is actually not so ugly as all that, It gets less ugly as the number of message types increases: to implement a new message type, you just have to add a value to your Enum and mark the new message handler class with an attribute.</p> <p>And there are circumstances where being able to load your message handlers from an assembly at runtime is a very powerful feature; you can have a single executable that behaves differently based on which message-handler assembly is installed.</p> <p>Start by creating an interface for the message handler (we'll call it <code>IMessageHandler</code>) and an Enum for the message type (we'll call it <code>MessageType</code>).</p> <p>Next, create a class called <code>MessageHandlerAttribute</code>:</p> <pre><code>public class MessageHandlerAttribute : System.Attribute { public MessageType MessageType { get; set; } } </code></pre> <p>Now implement each message handler as a separate class, and mark each class with its message type attribute. If a message handler can handle multiple types of message, you can put multiple attributes on it:</p> <pre><code>[MessageHandler(MessageType=MessageType.Login)] public class LoginMessageHandler : IMessageHandler { ... } </code></pre> <p>It's important that these message handlers all have parameterless constructors. I can't think of a good reason that you'd <em>want</em> a message handler's constructor to take parameters, but if any does, the code below can't handle it.</p> <p>Build all the message handlers into the same assembly, and be sure you have a way to know its path at runtime. (That's the big point of failure for this approach.)</p> <p>Now we can use Reflection to build a map of message handlers at runtime:</p> <pre><code>using System.Reflection; ... Assembly mhAssembly = Assembly.LoadFrom(mhAssemblyPath); Dictionary&lt;MessageType, IMessageHandler&gt; mhMap = new Dictionary&lt;MessageType, IMessageHandler&gt;(); foreach (Type t in mhAssembly.GetExportedTypes()) { if (t.GetInterface("IMessageHandler") != null) { MessageHandlerAttribute list = (MessageHandlerAttribute[])t.GetCustomAttributes( typeof(MessageHandlerAttribute), false); foreach (MessageHandlerAttribute att in list) { MessageType mt = att.MessageType; Debug.Assert(!mhMap.ContainsKey(mt)); IMessageHandler mh = mhAssembly.CreateInstance( t.FullName, true, BindingFlags.CreateInstance, null, new object[] { }, null, null); mhMap.Add(mt, mh); } } // depending on your application, you might want to check mhMap now to make // sure that every MessageType value is in it. } return mhMap; </code></pre> <p>Now when you get a message, you can handle it like this:</p> <pre><code>Debug.Assert(MhMap.ContainsKey(Message.MessageType)); IMessageHandler mh = MhMap[Message.MessageType]; mh.HandleMessage(Message); </code></pre> <p>This code's all based on code I have in a production system right now; I've changed it slightly (so that the message handlers implement an interface instead of deriving from an abstract class, and that it handles multiple message handler attributes), which has probably introduced bugs into it.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload