【问题标题】:Dynamic Double Dispatch without using dynamic keyword不使用动态关键字的动态双重调度
【发布时间】:2013-03-28 16:07:25
【问题描述】:

我正在尝试将 this 的动态双重调度示例移植到 C#。我已经完成了这个示例,但我觉得通过使用反射创建所需的处理程序并调用适当的方法,我已经简化了MessageBase 类中的DynamicDispatch 方法。任何人都可以就如何更改它提供一些建议吗?该示例使用 C++ dynamic_cast 运算符,我不确定 C# 的等价物是什么。我不确定我的解决方案是否正确/最佳。

注意:我使用的是 3.5,所以不能使用动态关键字

代码如下:

我的消息

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

消息库

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);

【问题讨论】:

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


    【解决方案1】:

    在 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>
    {
    }
    

    【讨论】:

    • 不幸的是,这不起作用,因为MessageTypeMessage 类型,因此不能转换为该类型。结果为空。
    • 错过了。省略处理程序详细信息,您可以像这样传递类型信息: class MessageBase { protected void DoDispatch(T m) { // ... } } class Message : MessageBase where T : class { public void Dispatch( ) { DoDispatch(this as T); } } 类 MyMessage : Message { }
    • +1 不错,是的,这似乎可以做到(尽管将 Derived 消息类型作为泛型类型传递似乎很奇怪)。谢谢!
    【解决方案2】:

    我发现这个 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();
            });
        }
    }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-16
      • 2023-03-21
      • 1970-01-01
      • 2011-01-31
      • 2015-08-05
      • 1970-01-01
      • 1970-01-01
      • 2011-06-01
      相关资源
      最近更新 更多