不使用动态关键字的动态双重调度

时间:2013-03-28 16:07:26

标签: c# dynamic .net-3.5 double-dispatch

我正在尝试将this动态双重调度的示例移植到C#。我已经有了实例,但我觉得我通过使用Reflection来创建所需的处理程序并调用适当的方法,使DynamicDispatch类中的MessageBase方法略微快捷。可以提供一些关于如何改变它的建议吗?该示例使用C ++ dynamic_cast运算符,我不确定C#的等价物是什么。我不确定我的解决方案是正确/最好的方式。

注意:我使用的是3.5,因此我无法使用动态关键字

以下是代码:

即时聊天

public interface IMessage
{
   void Dispatch(IHandler handler);
}

MessageBase

public abstract class MessageBase : IMessage
{
   public abstract void Dispatch(IHandler handler);

   // This is my concern, doesnt feel like the right way to do this
   protected void DynamicDispatch<MessageType>(IHandler handler, MessageType self)
   {
      // Get the messages derived type
      Type self_type = self.GetType();   
      // Create actual message specific handler
      Type message_handler = typeof(IMessageHandler<>).MakeGenericType(self_type);
      // Get the ProcessMessage method
      MethodInfo minfo = message_handler.GetMethod("ProcessMessage");
      try
      {
         // Invoke it with the message
         minfo.Invoke(handler, new object[] { self });
      }
      catch (TargetException ex)
      {
         // Ignore if method doesnt exist
      }
   }
}

消息

public class Message : MessageBase
{
   public override void Dispatch(IHandler handler)
   {
      DynamicDispatch(handler, this);
   }
}

IHandler

public interface IHandler
{
}

IMessageHandler

public interface IMessageHandler<MessageType> : IHandler
{
   void ProcessMessage(MessageType message);
}

DerivedMessageOne

public class DerivedMessageOne : Message
{
   public int MessageOneField;
}

DerivedMessageTwo

public class DerivedMessageTwo : Message
{
   public int MessageTwoField;
}

DerivedMessageHandlerOne

public class DerivedMessageHandlerOne : IMessageHandler<DerivedMessageOne>,
   IMessageHandler<DerivedMessageTwo>
{
   #region IMessageHandler<MessaegType> Members

   // ************ handle both messages *************** //
   public void ProcessMessage(DerivedMessageOne message)
   {
      // Received Message one, do soemthing with i
      int do_something_with_it = message.MessageOneField;
   } 

   public void ProcessMessage(DerivedMessageTwo message)
   {
      // Received Message two, do soemthing with i
   } 

   #endregion
}

DerivedMessageHandlerTwo

public class DerivedMessageHandlerTwo : IMessageHandler<DerivedMessageOne>
{
   #region IMessageHandler<MessaegType> Members

   // ************ handle just MessageOne *************** //
   public void ProcessMessage(DerivedMessageOne message)
   {
      // Received Message one, do soemthing with i
   }

   #endregion
}

测试用例

IMessage messageOne = new DerivedMessageOne();
IMessage messageTwo = new DerivedMessageTwo();
IHandler handlerOne = new DerivedMessageHandlerOne();
IHandler handlerTwo = new DerivedMessageHandlerTwo();

messageOne.Dispatch(handlerOne);
messageOne.Dispatch(handlerTwo);

messageTwo.Dispatch(handlerOne);
messageTwo.Dispatch(handlerTwo);

2 个答案:

答案 0 :(得分:2)

在C#中,您需要as运算符。     http://msdn.microsoft.com/en-us/library/vstudio/cscsdfbt.aspx

它的行为与C ++ dynamic_cast完全相同。

这里可以使用类似的东西:

IMessageHandler<MessageType> handlerTarget = handler as IMessageHandler<MessageType>;
handlerTarget.ProcessMessage(message);

如您所述,您必须将消息类型传递到消息库中,例如:

class MessageBase
{
    protected void DoDispatch<T>(T m)
    {
        // ...
    }
}
class Message<T> : MessageBase where T : class
{
    public void Dispatch()
    {
        DoDispatch<T>(this as T);
    }
}
class MyMessage : Message<MyMessage>
{
}

答案 1 :(得分:0)

我发现这个DoubleDispatch在没有动态的情况下工作正常:

namespace DoubleDispatch
{
public interface IMessage
{
    void Dispatch(HandlerBase handler);
}

public interface IHandler
{
    void HandleDefault(IMessage message);
}

public interface IHandler<in TMessage> : IHandler
    where TMessage : class, IMessage
{
    void HandleSpecific(TMessage message);
}

public interface IMessage<TMessage, THandler> : IMessage
    where TMessage : class, IMessage<TMessage, THandler>
    where THandler : class, IHandler<TMessage>
{
}

public abstract class HandlerBase : IHandler
{
    public void HandleDefault(IMessage message)
    {
        Console.WriteLine("HandlerBase process {0}", message.GetType().Name);
    }
}

public abstract class MessageBase<TMessage, THandler>
    : IMessage<TMessage, IHandler<TMessage>>
    where TMessage : class, IMessage, IMessage<TMessage, IHandler<TMessage>>
    where THandler : class, IHandler, IHandler<TMessage>
{
    public void Dispatch(HandlerBase handler)
    {
        var runtimeHandler = handler as THandler;
        if (runtimeHandler != null)
        {
            var runtimeMessage = this as TMessage;
            if (runtimeMessage != null)
            {
                runtimeHandler.HandleSpecific(runtimeMessage);
                return;
            }
        }
        handler.HandleDefault(this);
    }
}

public class FirstMessage : MessageBase<FirstMessage, IHandler<FirstMessage>>
{
}

public class SecondMessage : MessageBase<SecondMessage, IHandler<SecondMessage>>
{
}

public class FirstHandler : HandlerBase, IHandler<FirstMessage>
{
    public void HandleSpecific(FirstMessage message)
    {
        Console.WriteLine("FirstHandler process {0}", message.GetType().Name);
    }
}

public class SecondHandler : HandlerBase, IHandler<SecondMessage>
{
    public void HandleSpecific(SecondMessage message)
    {
        Console.WriteLine("SecondHandler process {0}", message.GetType().Name);
    }
}

public class ThirdHandler : HandlerBase, IHandler<FirstMessage>, IHandler<SecondMessage>
{
    public void HandleSpecific(FirstMessage message)
    {
        Console.WriteLine("ThirdHandler process {0}", message.GetType().Name);
    }

    public void HandleSpecific(SecondMessage message)
    {
        Console.WriteLine("ThirdHandler process {0}", message.GetType().Name);
    }
}

public class EmptyHandler : HandlerBase
{
}

public static class DoubleDispatch
{
    public static void Test()
    {
        var handlers = new HandlerBase[]
        {
            new FirstHandler(),
            new SecondHandler(),
            new ThirdHandler(), 
            new EmptyHandler(), 
        };

        var messages = new IMessage[]
        {
            new FirstMessage(),
            new SecondMessage(), 
        };

        Array.ForEach(messages, m =>
        {
            Array.ForEach(handlers, m.Dispatch);
            Console.WriteLine();
        });
    }
}
}